@vesk/compiler 0.1.14 → 0.2.8
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/dist/api-routes.d.ts +6 -1
- package/dist/api-routes.d.ts.map +1 -1
- package/dist/api-routes.js +52 -7
- package/dist/client-codegen.d.ts +15 -0
- package/dist/client-codegen.d.ts.map +1 -1
- package/dist/client-codegen.js +73 -10
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +25 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +33 -4
- package/dist/ir-generator.d.ts.map +1 -1
- package/dist/ir-generator.js +109 -1
- package/dist/md-inline.d.ts +17 -0
- package/dist/md-inline.d.ts.map +1 -0
- package/dist/md-inline.js +149 -0
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +42 -7
- package/dist/router.d.ts +2 -0
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +48 -3
- package/dist/server-codegen.d.ts +1 -1
- package/dist/server-codegen.d.ts.map +1 -1
- package/dist/server-codegen.js +1 -1
- package/dist/server-jsgen.js +1 -1
- package/dist/server-render.d.ts.map +1 -1
- package/dist/server-render.js +14 -8
- package/dist/server-utils.d.ts +31 -0
- package/dist/server-utils.d.ts.map +1 -1
- package/dist/server-utils.js +478 -52
- package/dist/typecheck.d.ts +2 -2
- package/dist/typecheck.d.ts.map +1 -1
- package/dist/typecheck.js +66 -27
- package/dist/types.d.ts +4 -129
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/vesk-plugin.d.ts +2 -0
- package/dist/vesk-plugin.d.ts.map +1 -1
- package/dist/vesk-plugin.js +152 -0
- package/dist/vsk-tsx.d.ts.map +1 -1
- package/dist/vsk-tsx.js +15 -5
- package/package.json +2 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
// =============================================================
|
|
4
|
+
// Inline resolution of <Md content="path/to/file.md"> attributes.
|
|
5
|
+
//
|
|
6
|
+
// When a `content="..."` attribute on an <Md> element is a static string
|
|
7
|
+
// ending in .md/.markdown, the file is read at COMPILE time and inlined
|
|
8
|
+
// into the source as a module-level const, so SSR, client chunks, SSG and
|
|
9
|
+
// production all work without runtime filesystem access.
|
|
10
|
+
//
|
|
11
|
+
// Resolution order for the specifier:
|
|
12
|
+
// - './x.md' / '../x.md' → relative to the importing .vsk file
|
|
13
|
+
// - '/x.md' → candidate roots (project dir / public),
|
|
14
|
+
// tried as <root>/public/x.md then <root>/x.md
|
|
15
|
+
// =============================================================
|
|
16
|
+
const MD_EXT_MARKER = '.md';
|
|
17
|
+
function looksLikeMarkdownPath(value) {
|
|
18
|
+
const v = value.trim();
|
|
19
|
+
if (v.length === 0 || v.length > 4096)
|
|
20
|
+
return false;
|
|
21
|
+
if (v.includes('\n') || v.includes('\r'))
|
|
22
|
+
return false;
|
|
23
|
+
const lower = v.toLowerCase();
|
|
24
|
+
return lower.endsWith(MD_EXT_MARKER) || lower.endsWith('.markdown');
|
|
25
|
+
}
|
|
26
|
+
function isRelativeSpecifier(v) {
|
|
27
|
+
return v.startsWith('./') || v.startsWith('../');
|
|
28
|
+
}
|
|
29
|
+
/** Walks up from `start` looking for the first directory containing `marker`. */
|
|
30
|
+
function findUp(start, marker) {
|
|
31
|
+
let cur = start;
|
|
32
|
+
for (let i = 0; i < 32; i++) {
|
|
33
|
+
if (existsSync(join(cur, marker)))
|
|
34
|
+
return cur;
|
|
35
|
+
const parent = dirname(cur);
|
|
36
|
+
if (parent === cur)
|
|
37
|
+
return null;
|
|
38
|
+
cur = parent;
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Rewrites static `content="<file>.md"` attributes on <Md> elements into
|
|
44
|
+
* inlined constants. Returns the transformed source; when nothing matches,
|
|
45
|
+
* the original string is returned untouched.
|
|
46
|
+
*
|
|
47
|
+
* `mdRoots` are candidate project roots for absolute-style specifiers
|
|
48
|
+
* ("/x.md"); each root is tried as <root>/public<x> and <root><x>.
|
|
49
|
+
*/
|
|
50
|
+
export function inlineMdContentAttrs(source, importerDir, mdRoots = []) {
|
|
51
|
+
if (!source.includes('content=') && !source.includes('content ='))
|
|
52
|
+
return source;
|
|
53
|
+
if (!importerDir && mdRoots.length === 0)
|
|
54
|
+
return source;
|
|
55
|
+
let out = '';
|
|
56
|
+
let i = 0;
|
|
57
|
+
const n = source.length;
|
|
58
|
+
let counter = 0;
|
|
59
|
+
const decls = [];
|
|
60
|
+
const prefix = 'content=';
|
|
61
|
+
while (i < n) {
|
|
62
|
+
if (source.startsWith(prefix, i)) {
|
|
63
|
+
// attribute position: ensure this is a JSX-ish attribute (preceded by whitespace/start)
|
|
64
|
+
const prev = i > 0 ? source[i - 1] : ' ';
|
|
65
|
+
if (prev === ' ' || prev === '\t' || prev === '\n' || prev === '\r') {
|
|
66
|
+
let j = i + prefix.length;
|
|
67
|
+
const quote = source[j];
|
|
68
|
+
if (quote === '"' || quote === "'") {
|
|
69
|
+
j++;
|
|
70
|
+
const valStart = j;
|
|
71
|
+
while (j < n && source[j] !== quote)
|
|
72
|
+
j++;
|
|
73
|
+
if (j < n) {
|
|
74
|
+
const value = source.slice(valStart, j);
|
|
75
|
+
if (looksLikeMarkdownPath(value)) {
|
|
76
|
+
const abs = resolveMdPath(value, importerDir, mdRoots);
|
|
77
|
+
if (abs !== null) {
|
|
78
|
+
let contents;
|
|
79
|
+
try {
|
|
80
|
+
contents = readFileSync(abs, 'utf-8');
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
out += source[i];
|
|
84
|
+
i++;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const varName = `__vesk_md_${counter++}`;
|
|
88
|
+
decls.push(`const ${varName} = ${JSON.stringify(contents)};`);
|
|
89
|
+
out += `${prefix}{${varName}}`;
|
|
90
|
+
i = j + 1;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
out += source[i];
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
if (decls.length === 0)
|
|
102
|
+
return source;
|
|
103
|
+
return decls.join('\n') + '\n' + out;
|
|
104
|
+
}
|
|
105
|
+
function resolveMdPath(specifier, importerDir, roots) {
|
|
106
|
+
const spec = specifier.trim();
|
|
107
|
+
if (isRelativeSpecifier(spec)) {
|
|
108
|
+
if (!importerDir)
|
|
109
|
+
return null;
|
|
110
|
+
const abs = resolve(importerDir, spec);
|
|
111
|
+
return existsSync(abs) ? abs : null;
|
|
112
|
+
}
|
|
113
|
+
if (spec.startsWith('/')) {
|
|
114
|
+
for (const root of roots) {
|
|
115
|
+
const pub = join(root, 'public', spec);
|
|
116
|
+
if (existsSync(pub))
|
|
117
|
+
return pub;
|
|
118
|
+
const direct = join(root, spec);
|
|
119
|
+
if (existsSync(direct))
|
|
120
|
+
return direct;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Candidate project roots for absolute-style specifiers: walks up from
|
|
127
|
+
* `dir` collecting directories that contain a package.json (bounded).
|
|
128
|
+
*/
|
|
129
|
+
export function guessProjectRoots(dir) {
|
|
130
|
+
if (!dir)
|
|
131
|
+
return [];
|
|
132
|
+
const roots = [];
|
|
133
|
+
let cur = dir;
|
|
134
|
+
for (let i = 0; i < 6; i++) {
|
|
135
|
+
roots.push(cur);
|
|
136
|
+
if (existsSync(join(cur, 'package.json')))
|
|
137
|
+
break;
|
|
138
|
+
const parent = dirname(cur);
|
|
139
|
+
if (parent === cur)
|
|
140
|
+
break;
|
|
141
|
+
cur = parent;
|
|
142
|
+
}
|
|
143
|
+
return roots;
|
|
144
|
+
}
|
|
145
|
+
/** Convenience wrapper used by pipelines that already know the file dir. */
|
|
146
|
+
export function inlineMdImportsFrom(source, importerFile, mdRoots = []) {
|
|
147
|
+
const dir = importerFile ? dirname(importerFile) : null;
|
|
148
|
+
return inlineMdContentAttrs(source, dir, mdRoots.length > 0 ? mdRoots : [dir || process.cwd()]);
|
|
149
|
+
}
|
package/dist/middleware.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAiC,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAEtG,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,eAAe,EAAE,CAsE7G;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAiC,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAEtG,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,eAAe,EAAE,CAsE7G;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,CAiBvG;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,eAAe,EAAE,EACxB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC;IAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAkIzH"}
|
package/dist/middleware.js
CHANGED
|
@@ -75,7 +75,9 @@ export async function loadMiddleware(sourcePath) {
|
|
|
75
75
|
}
|
|
76
76
|
if (sourcePath.endsWith('.js') || sourcePath.endsWith('.ts')) {
|
|
77
77
|
try {
|
|
78
|
-
const
|
|
78
|
+
const url = new URL('file://' + sourcePath);
|
|
79
|
+
url.searchParams.set('t', String(Date.now()) + Math.random().toString(36).slice(2));
|
|
80
|
+
const mod = await import(url.href);
|
|
79
81
|
return (mod.middleware || mod.default || null);
|
|
80
82
|
}
|
|
81
83
|
catch {
|
|
@@ -132,21 +134,37 @@ export async function executeMiddlewareChain(chain, request, params, options = {
|
|
|
132
134
|
}
|
|
133
135
|
let rewriteUrl = null;
|
|
134
136
|
if (chain.length === 0) {
|
|
137
|
+
if (onLast) {
|
|
138
|
+
const prev = globalThis.__vesk_request;
|
|
139
|
+
globalThis.__vesk_request = ctx;
|
|
140
|
+
try {
|
|
141
|
+
const loneResponse = await onLast(rewriteUrl, ctx);
|
|
142
|
+
return { response: loneResponse ?? null, redirected: !!loneResponse && loneResponse.status >= 300 && loneResponse.status < 400, locals, rewriteUrl };
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
globalThis.__vesk_request = prev;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
135
148
|
return { response: null, redirected: false, locals, rewriteUrl: null };
|
|
136
149
|
}
|
|
137
150
|
async function runMiddleware(index) {
|
|
138
151
|
if (index >= chain.length) {
|
|
139
152
|
if (onLast) {
|
|
140
|
-
return onLast(rewriteUrl);
|
|
153
|
+
return onLast(rewriteUrl, ctx);
|
|
141
154
|
}
|
|
142
|
-
return
|
|
155
|
+
return null;
|
|
143
156
|
}
|
|
144
157
|
const { sourcePath } = chain[index];
|
|
145
158
|
const fn = await loadMiddleware(sourcePath);
|
|
146
159
|
if (!fn) {
|
|
147
160
|
return runMiddleware(index + 1);
|
|
148
161
|
}
|
|
162
|
+
let nextCalled = false;
|
|
163
|
+
let nextResult = null;
|
|
149
164
|
async function next(rewrite) {
|
|
165
|
+
if (nextCalled)
|
|
166
|
+
return null;
|
|
167
|
+
nextCalled = true;
|
|
150
168
|
if (rewrite) {
|
|
151
169
|
rewriteUrl = rewrite;
|
|
152
170
|
try {
|
|
@@ -158,14 +176,21 @@ export async function executeMiddlewareChain(chain, request, params, options = {
|
|
|
158
176
|
ctx.url = new URL(rewrite, base.origin);
|
|
159
177
|
}
|
|
160
178
|
}
|
|
161
|
-
|
|
179
|
+
nextResult = await runMiddleware(index + 1);
|
|
180
|
+
return nextResult;
|
|
162
181
|
}
|
|
163
182
|
try {
|
|
164
183
|
const result = await fn(ctx, next);
|
|
165
184
|
if (result instanceof Response) {
|
|
166
185
|
return result;
|
|
167
186
|
}
|
|
168
|
-
|
|
187
|
+
if (nextResult !== null && nextResult instanceof Response) {
|
|
188
|
+
return nextResult;
|
|
189
|
+
}
|
|
190
|
+
if (!nextCalled) {
|
|
191
|
+
return runMiddleware(index + 1);
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
169
194
|
}
|
|
170
195
|
catch (e) {
|
|
171
196
|
const err = e;
|
|
@@ -178,6 +203,16 @@ export async function executeMiddlewareChain(chain, request, params, options = {
|
|
|
178
203
|
throw e;
|
|
179
204
|
}
|
|
180
205
|
}
|
|
181
|
-
|
|
182
|
-
|
|
206
|
+
// Ensure the outermost ctx is available as __vesk_request during the chain,
|
|
207
|
+
// so that server locals / onLast rendering sees middleware locals.
|
|
208
|
+
const prevOuter = globalThis.__vesk_request;
|
|
209
|
+
globalThis.__vesk_request = ctx;
|
|
210
|
+
let response;
|
|
211
|
+
try {
|
|
212
|
+
response = await runMiddleware(0);
|
|
213
|
+
}
|
|
214
|
+
finally {
|
|
215
|
+
globalThis.__vesk_request = prevOuter;
|
|
216
|
+
}
|
|
217
|
+
return { response, redirected: !!response && response.status >= 300 && response.status < 400, locals, rewriteUrl };
|
|
183
218
|
}
|
package/dist/router.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export interface MatchResult {
|
|
|
15
15
|
* handled correctly; falls back to the old regex + brace balance when the
|
|
16
16
|
* source does not parse.
|
|
17
17
|
*/
|
|
18
|
+
/** Collapses runs of `/` into single slashes (no regex). */
|
|
19
|
+
export declare function collapseSlashes(p: string): string;
|
|
18
20
|
export declare function extractMiddlewareParts(src: string): {
|
|
19
21
|
params: string;
|
|
20
22
|
body: string;
|
package/dist/router.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAM1D,MAAM,WAAW,WAAW;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAM1D,MAAM,WAAW,WAAW;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;GAMG;AACH,4DAA4D;AAC5D,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAajD;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAqB3F;AAkDD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUnE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,SAAS,EAAE,CAKjF;AAMD,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CA2BzE;AA2HD,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBrE;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAkCnG;AAoBD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAiFhF"}
|
package/dist/router.js
CHANGED
|
@@ -11,6 +11,23 @@ import { hasTsSyntax, stripCodeTypes } from '@vesk/compiler/src/strip-ts';
|
|
|
11
11
|
* handled correctly; falls back to the old regex + brace balance when the
|
|
12
12
|
* source does not parse.
|
|
13
13
|
*/
|
|
14
|
+
/** Collapses runs of `/` into single slashes (no regex). */
|
|
15
|
+
export function collapseSlashes(p) {
|
|
16
|
+
let out = '';
|
|
17
|
+
let prevSlash = false;
|
|
18
|
+
for (const c of p) {
|
|
19
|
+
if (c === '/') {
|
|
20
|
+
if (!prevSlash)
|
|
21
|
+
out += '/';
|
|
22
|
+
prevSlash = true;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
out += c;
|
|
26
|
+
prevSlash = false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
14
31
|
export function extractMiddlewareParts(src) {
|
|
15
32
|
try {
|
|
16
33
|
let ast = parse(src, { filename: 'middleware.ts' });
|
|
@@ -180,6 +197,8 @@ function scanDirectory(rootDir, dir, parentPath, options) {
|
|
|
180
197
|
let hasLoading = false;
|
|
181
198
|
let hasError = false;
|
|
182
199
|
let hasNotFound = false;
|
|
200
|
+
let hasOffline = false;
|
|
201
|
+
let hasNetwork = false;
|
|
183
202
|
let hasMiddleware = false;
|
|
184
203
|
for (const entry of entries) {
|
|
185
204
|
if (entry === 'layout.vsk') {
|
|
@@ -202,6 +221,14 @@ function scanDirectory(rootDir, dir, parentPath, options) {
|
|
|
202
221
|
hasNotFound = true;
|
|
203
222
|
continue;
|
|
204
223
|
}
|
|
224
|
+
if (entry === 'offline.vsk') {
|
|
225
|
+
hasOffline = true;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (entry === 'network.vsk') {
|
|
229
|
+
hasNetwork = true;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
205
232
|
if (entry === 'middleware.ts') {
|
|
206
233
|
hasMiddleware = true;
|
|
207
234
|
continue;
|
|
@@ -235,7 +262,7 @@ function scanDirectory(rootDir, dir, parentPath, options) {
|
|
|
235
262
|
: (parentPath || '/');
|
|
236
263
|
const node = {
|
|
237
264
|
path: seg,
|
|
238
|
-
fullPath: fullPath
|
|
265
|
+
fullPath: collapseSlashes(fullPath) || '/',
|
|
239
266
|
isGroup,
|
|
240
267
|
isDynamic,
|
|
241
268
|
isCatchAll,
|
|
@@ -244,6 +271,8 @@ function scanDirectory(rootDir, dir, parentPath, options) {
|
|
|
244
271
|
loading: hasLoading ? extractComponentName(dir, 'loading', rootDir) : null,
|
|
245
272
|
error: hasError ? extractComponentName(dir, 'error', rootDir) : null,
|
|
246
273
|
notFound: hasNotFound ? extractComponentName(dir, 'not-found', rootDir) : null,
|
|
274
|
+
offline: hasOffline ? extractComponentName(dir, 'offline', rootDir) : null,
|
|
275
|
+
network: hasNetwork ? extractComponentName(dir, 'network', rootDir) : null,
|
|
247
276
|
hasMiddleware,
|
|
248
277
|
children: [],
|
|
249
278
|
sourceDir: dir,
|
|
@@ -292,6 +321,10 @@ function extractComponentName(dir, type, rootDir) {
|
|
|
292
321
|
return 'Error_' + capitalized;
|
|
293
322
|
if (type === 'not-found')
|
|
294
323
|
return 'NotFound_' + capitalized;
|
|
324
|
+
if (type === 'offline')
|
|
325
|
+
return 'Offline_' + capitalized;
|
|
326
|
+
if (type === 'network')
|
|
327
|
+
return 'Network_' + capitalized;
|
|
295
328
|
return type + '_' + capitalized;
|
|
296
329
|
}
|
|
297
330
|
export function collectSources(tree) {
|
|
@@ -308,6 +341,10 @@ export function collectSources(tree) {
|
|
|
308
341
|
map.set(node.error, join(node.sourceDir, 'error.vsk'));
|
|
309
342
|
if (node.notFound)
|
|
310
343
|
map.set(node.notFound, join(node.sourceDir, 'not-found.vsk'));
|
|
344
|
+
if (node.offline)
|
|
345
|
+
map.set(node.offline, join(node.sourceDir, 'offline.vsk'));
|
|
346
|
+
if (node.network)
|
|
347
|
+
map.set(node.network, join(node.sourceDir, 'network.vsk'));
|
|
311
348
|
walk(node.children);
|
|
312
349
|
}
|
|
313
350
|
}
|
|
@@ -328,9 +365,13 @@ export function generateRouteManifest(tree, options = {}) {
|
|
|
328
365
|
parts.push(`error: ${node.error}`);
|
|
329
366
|
if (node.notFound)
|
|
330
367
|
parts.push(`notFound: ${node.notFound}`);
|
|
368
|
+
if (node.offline)
|
|
369
|
+
parts.push(`offline: ${node.offline}`);
|
|
370
|
+
if (node.network)
|
|
371
|
+
parts.push(`network: ${node.network}`);
|
|
331
372
|
if (node.children.length > 0) {
|
|
332
|
-
const childCodes = node.children.map(c => genNode(c));
|
|
333
|
-
parts.push(`children: [\n${childCodes.map(c => '\t\t' + c).join(',\n')}\n\t]`);
|
|
373
|
+
const childCodes = node.children.map((c) => genNode(c));
|
|
374
|
+
parts.push(`children: [\n${childCodes.map((c) => '\t\t' + c).join(',\n')}\n\t]`);
|
|
334
375
|
}
|
|
335
376
|
const pathStr = JSON.stringify(node.fullPath);
|
|
336
377
|
const groupStr = node.isGroup ? `, isGroup: true` : '';
|
|
@@ -363,6 +404,10 @@ function flattenSources(tree) {
|
|
|
363
404
|
map.set(node.error, node.sourceDir + '/error.vsk');
|
|
364
405
|
if (node.notFound)
|
|
365
406
|
map.set(node.notFound, node.sourceDir + '/not-found.vsk');
|
|
407
|
+
if (node.offline)
|
|
408
|
+
map.set(node.offline, node.sourceDir + '/offline.vsk');
|
|
409
|
+
if (node.network)
|
|
410
|
+
map.set(node.network, node.sourceDir + '/network.vsk');
|
|
366
411
|
walk(node.children);
|
|
367
412
|
}
|
|
368
413
|
}
|
package/dist/server-codegen.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { setRuntimeModule, prettifyHtml, isStatic, escapeHtml, raw, exprJS, tryEvalExpr, childrenToHTML, extractTopLevelNames, extractRuntimeNames, buildParamInit, resolveComponentName, loadRuntimeImports, evalTopLevelCode, callStaticProps, callLoadFunction, __vskHydrate, __vskId, __vskImportedNames, resetVskState, setVskHydrate, setVskImportedNames, securityHeaders, corsHeaders, corsPreflight, csrfToken, verifyCsrfToken, csrfGuard, signCookie, unsignCookie, setSignedCookie, readSignedCookie, securityComment, redactLog, setRedactLogging, createRateLimiter, getClientIp, getClientProtocol, applyTrustProxy, } from '@vesk/compiler/src/server-utils';
|
|
1
|
+
export { setRuntimeModule, prettifyHtml, isStatic, escapeHtml, quoteAttr, safeJsonForScript, randomToken, DEFAULT_MAX_BODY_BYTES, raw, exprJS, tryEvalExpr, childrenToHTML, extractTopLevelNames, extractRuntimeNames, buildParamInit, resolveComponentName, loadRuntimeImports, evalTopLevelCode, callStaticProps, callLoadFunction, __vskHydrate, __vskId, __vskImportedNames, resetVskState, setVskHydrate, setVskImportedNames, securityHeaders, corsHeaders, corsPreflight, csrfToken, verifyCsrfToken, csrfGuard, csrfHmac, assertSameOrigin, signCookie, unsignCookie, setSignedCookie, readSignedCookie, securityComment, redactLog, setRedactLogging, createRateLimiter, getClientIp, getClientProtocol, applyTrustProxy, } from '@vesk/compiler/src/server-utils';
|
|
2
2
|
export { renderHeadHtml, mergeHeadHtml, } from '@vesk/compiler/src/server-head';
|
|
3
3
|
export { irNodeToJS, generateFunctionBody, buildComponentMap, } from '@vesk/compiler/src/server-jsgen';
|
|
4
4
|
export { compileFile, render, renderPage, ssg, renderFullPage, renderPageStream, buildDataScripts, } from '@vesk/compiler/src/server-render';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-codegen.d.ts","sourceRoot":"","sources":["../src/server-codegen.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,GAAG,EACH,MAAM,EACN,WAAW,EACX,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,eAAe,EACf,WAAW,EACX,aAAa,EACb,SAAS,EACT,eAAe,EACf,SAAS,EACT,UAAU,EACV,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,eAAe,GAChB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,cAAc,EACd,aAAa,GACd,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,WAAW,EACX,MAAM,EACN,UAAU,EACV,GAAG,EACH,cAAc,EACd,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,kCAAkC,CAAC"}
|
|
1
|
+
{"version":3,"file":"server-codegen.d.ts","sourceRoot":"","sources":["../src/server-codegen.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,SAAS,EACT,iBAAiB,EACjB,WAAW,EACX,sBAAsB,EACtB,GAAG,EACH,MAAM,EACN,WAAW,EACX,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,eAAe,EACf,WAAW,EACX,aAAa,EACb,SAAS,EACT,eAAe,EACf,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,eAAe,GAChB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,cAAc,EACd,aAAa,GACd,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,WAAW,EACX,MAAM,EACN,UAAU,EACV,GAAG,EACH,cAAc,EACd,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,kCAAkC,CAAC"}
|
package/dist/server-codegen.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { setRuntimeModule, prettifyHtml, isStatic, escapeHtml, raw, exprJS, tryEvalExpr, childrenToHTML, extractTopLevelNames, extractRuntimeNames, buildParamInit, resolveComponentName, loadRuntimeImports, evalTopLevelCode, callStaticProps, callLoadFunction, __vskHydrate, __vskId, __vskImportedNames, resetVskState, setVskHydrate, setVskImportedNames, securityHeaders, corsHeaders, corsPreflight, csrfToken, verifyCsrfToken, csrfGuard, signCookie, unsignCookie, setSignedCookie, readSignedCookie, securityComment, redactLog, setRedactLogging, createRateLimiter, getClientIp, getClientProtocol, applyTrustProxy, } from '@vesk/compiler/src/server-utils';
|
|
1
|
+
export { setRuntimeModule, prettifyHtml, isStatic, escapeHtml, quoteAttr, safeJsonForScript, randomToken, DEFAULT_MAX_BODY_BYTES, raw, exprJS, tryEvalExpr, childrenToHTML, extractTopLevelNames, extractRuntimeNames, buildParamInit, resolveComponentName, loadRuntimeImports, evalTopLevelCode, callStaticProps, callLoadFunction, __vskHydrate, __vskId, __vskImportedNames, resetVskState, setVskHydrate, setVskImportedNames, securityHeaders, corsHeaders, corsPreflight, csrfToken, verifyCsrfToken, csrfGuard, csrfHmac, assertSameOrigin, signCookie, unsignCookie, setSignedCookie, readSignedCookie, securityComment, redactLog, setRedactLogging, createRateLimiter, getClientIp, getClientProtocol, applyTrustProxy, } from '@vesk/compiler/src/server-utils';
|
|
2
2
|
export { renderHeadHtml, mergeHeadHtml, } from '@vesk/compiler/src/server-head';
|
|
3
3
|
export { irNodeToJS, generateFunctionBody, buildComponentMap, } from '@vesk/compiler/src/server-jsgen';
|
|
4
4
|
export { compileFile, render, renderPage, ssg, renderFullPage, renderPageStream, buildDataScripts, } from '@vesk/compiler/src/server-render';
|
package/dist/server-jsgen.js
CHANGED
|
@@ -378,7 +378,7 @@ export function generateFunctionBody(comp, importedNames) {
|
|
|
378
378
|
lines.push(`const __prev = __ga();`);
|
|
379
379
|
lines.push(`__sa({ c: null, p: __prev });`);
|
|
380
380
|
lines.push(`try {`);
|
|
381
|
-
lines.push(`const __escape = (s) => String(s).
|
|
381
|
+
lines.push(`const __escape = (s) => { s = String(s); return s.split('&').join('&').split('<').join('<').split('>').join('>').split(${JSON.stringify('"')}).join('"'); };`);
|
|
382
382
|
lines.push(`const raw = (s) => s == null ? '' : String(s);`);
|
|
383
383
|
lines.push(`const __tk = globalThis.__vsk_ssr_token || '';`);
|
|
384
384
|
if (asyncMode) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-render.d.ts","sourceRoot":"","sources":["../src/server-render.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAqB,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"server-render.d.ts","sourceRoot":"","sources":["../src/server-render.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAqB,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAgBvJ,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,iBAAiB,CAEhG;AA8BD,wBAAgB,MAAM,CACpB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAa,EAC3C,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACpC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC1B;AAED,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAa,EAC3C,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACpC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiE9C;AAcD,wBAAsB,GAAG,CACvB,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,EACtB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAO,GAC9G,OAAO,CAAC,SAAS,CAAC,CA8DpB;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EACpD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,QAAQ,CAAC,EAAE,kBAAkB,GAAG,IAAI,GACnC,MAAM,EAAE,CAuBV;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAa,EAC3C,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,MAAM,CAAC,CAoEjB;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAa,EAC3C,OAAO,GAAE,eAAoB,GAC5B,cAAc,CAAC,MAAM,CAAC,CAkFxB"}
|
package/dist/server-render.js
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
2
3
|
import { MapRegion } from '@vesk/compiler/src/ir';
|
|
3
4
|
import { parse } from '@vesk/compiler/src/parser';
|
|
4
5
|
import { generateIR } from '@vesk/compiler/src/ir-generator';
|
|
5
6
|
import { compileClient, isStaticIR } from '@vesk/compiler/src/client-codegen';
|
|
6
|
-
import { isStatic, prettifyHtml, resetVskState, loadRuntimeImports, evalTopLevelCode, callStaticProps, callLoadFunction, } from '@vesk/compiler/src/server-utils';
|
|
7
|
+
import { isStatic, prettifyHtml, resetVskState, loadRuntimeImports, evalTopLevelCode, callStaticProps, callLoadFunction, safeJsonForScript, quoteAttr, } from '@vesk/compiler/src/server-utils';
|
|
7
8
|
import { renderHeadHtml, mergeHeadHtml } from '@vesk/compiler/src/server-head';
|
|
8
9
|
import { buildComponentMap } from '@vesk/compiler/src/server-jsgen';
|
|
9
10
|
import { transformTopLevelForActions } from '@vesk/compiler/src/actions';
|
|
10
11
|
import { collectVskImportPaths } from '@vesk/compiler/src/vsk-imports';
|
|
12
|
+
import { inlineMdImportsFrom, guessProjectRoots } from '@vesk/compiler/src/md-inline';
|
|
11
13
|
import { withSsrStore, ssrSink } from '@vesk/compiler/src/ssr-store';
|
|
12
14
|
export function compileFile(source, options) {
|
|
13
15
|
return compileFileInternal(source, options?.sourcePath, new Set());
|
|
14
16
|
}
|
|
15
17
|
function compileFileInternal(source, sourcePath, seenImportFiles) {
|
|
18
|
+
if (sourcePath) {
|
|
19
|
+
const dir = dirname(sourcePath);
|
|
20
|
+
source = inlineMdImportsFrom(source, sourcePath, guessProjectRoots(dir));
|
|
21
|
+
}
|
|
16
22
|
const ast = parse(source);
|
|
17
23
|
const ir = generateIR(ast, source);
|
|
18
24
|
const componentMap = buildComponentMap(ir, true);
|
|
@@ -190,7 +196,7 @@ export async function ssg(source, componentName, customProps, options = {}) {
|
|
|
190
196
|
const clientCode = needsClient
|
|
191
197
|
? compileClient(source, null, { hydrate: true })
|
|
192
198
|
: '';
|
|
193
|
-
const serializedProps = JSON.stringify(props);
|
|
199
|
+
const serializedProps = safeJsonForScript(JSON.stringify(props));
|
|
194
200
|
const hasClient = clientCode.length > 0;
|
|
195
201
|
const scriptBlock = hasClient
|
|
196
202
|
? `\n<script>const __vesk_props = ${serializedProps};\n<\/script>\n<script>${clientCode}</script>\n`
|
|
@@ -240,9 +246,9 @@ export function buildDataScripts(ssrProps, ssrData, external) {
|
|
|
240
246
|
}
|
|
241
247
|
const scripts = [];
|
|
242
248
|
if (hasProps)
|
|
243
|
-
scripts.push(`\t<script>const __vesk_props = ${JSON.stringify(payload.props)};</script>`);
|
|
249
|
+
scripts.push(`\t<script>const __vesk_props = ${safeJsonForScript(JSON.stringify(payload.props))};</script>`);
|
|
244
250
|
if (hasData)
|
|
245
|
-
scripts.push(`\t<script>globalThis.__vsk_ssr_data = ${JSON.stringify(ssrData)};</script>`);
|
|
251
|
+
scripts.push(`\t<script>globalThis.__vsk_ssr_data = ${safeJsonForScript(JSON.stringify(ssrData))};</script>`);
|
|
246
252
|
return scripts;
|
|
247
253
|
}
|
|
248
254
|
export async function renderFullPage(source, componentName, props = {}, registry = new Map(), options = {}) {
|
|
@@ -293,9 +299,9 @@ export async function renderFullPage(source, componentName, props = {}, registry
|
|
|
293
299
|
if (options.security) {
|
|
294
300
|
const sec = options.security;
|
|
295
301
|
if (sec.referrerPolicy !== false)
|
|
296
|
-
headLines.push(`\t<meta name="referrer" content="${sec.referrerPolicy || 'strict-origin-when-cross-origin'}" />`);
|
|
302
|
+
headLines.push(`\t<meta name="referrer" content="${quoteAttr(sec.referrerPolicy || 'strict-origin-when-cross-origin')}" />`);
|
|
297
303
|
if (sec.contentSecurityPolicy !== false)
|
|
298
|
-
headLines.push(`\t<meta http-equiv="Content-Security-Policy" content="${(sec.contentSecurityPolicy || "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'")
|
|
304
|
+
headLines.push(`\t<meta http-equiv="Content-Security-Policy" content="${quoteAttr(sec.contentSecurityPolicy || "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'")}" />`);
|
|
299
305
|
if (sec.autoEscape !== false)
|
|
300
306
|
headLines.push(`\t<!-- vesk: auto-escape enabled -->`);
|
|
301
307
|
}
|
|
@@ -348,9 +354,9 @@ export function renderPageStream(source, componentName, props = {}, registry = n
|
|
|
348
354
|
if (options.security) {
|
|
349
355
|
const sec = options.security;
|
|
350
356
|
if (sec.referrerPolicy !== false)
|
|
351
|
-
yield `\t<meta name="referrer" content="${sec.referrerPolicy || 'strict-origin-when-cross-origin'}" />\n`;
|
|
357
|
+
yield `\t<meta name="referrer" content="${quoteAttr(sec.referrerPolicy || 'strict-origin-when-cross-origin')}" />\n`;
|
|
352
358
|
if (sec.contentSecurityPolicy !== false)
|
|
353
|
-
yield `\t<meta http-equiv="Content-Security-Policy" content="${(sec.contentSecurityPolicy || "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'")
|
|
359
|
+
yield `\t<meta http-equiv="Content-Security-Policy" content="${quoteAttr(sec.contentSecurityPolicy || "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'")}" />\n`;
|
|
354
360
|
if (sec.autoEscape !== false)
|
|
355
361
|
yield `\t<!-- vesk: auto-escape enabled -->\n`;
|
|
356
362
|
}
|
package/dist/server-utils.d.ts
CHANGED
|
@@ -12,12 +12,36 @@ export declare function nextVskId(): number;
|
|
|
12
12
|
export declare function prettifyHtml(html: string): string;
|
|
13
13
|
export declare function isStatic(body: IRNode[]): boolean;
|
|
14
14
|
export declare function escapeHtml(str: string): string;
|
|
15
|
+
/** Escapes double quotes for embedding text inside a double-quoted attribute. */
|
|
16
|
+
export declare function quoteAttr(str: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Makes JSON output safe to embed inside an inline <script> block.
|
|
19
|
+
* Escapes `<` (breaks out of script context via `</script>`), line
|
|
20
|
+
* separators U+2028/U+2029 (valid JSON but invalid JS string literals).
|
|
21
|
+
*/
|
|
22
|
+
export declare function safeJsonForScript(json: string): string;
|
|
23
|
+
/** Cryptographically strong random hex token (WebCrypto CSPRNG). */
|
|
24
|
+
export declare function randomToken(bytes?: number): string;
|
|
25
|
+
/** Default request-body cap (bytes) for servers and action/API handlers. */
|
|
26
|
+
export declare const DEFAULT_MAX_BODY_BYTES: number;
|
|
15
27
|
export declare function redactLog(str: string): string;
|
|
16
28
|
export declare function setRedactLogging(enabled: boolean): void;
|
|
17
29
|
export declare function raw(value: unknown): string;
|
|
30
|
+
/** HMAC-SHA256 over `value` keyed by `secret`, hex-encoded. */
|
|
31
|
+
export declare function csrfHmac(value: string, secret: string): string;
|
|
18
32
|
export declare function csrfToken(sessionId?: string, host?: string): string;
|
|
19
33
|
export declare function verifyCsrfToken(token: string, host?: string): boolean;
|
|
20
34
|
export declare function csrfGuard(request: Record<string, unknown>, host?: string): void;
|
|
35
|
+
/** Extracts the authority (host[:port]) part of an absolute URL. */
|
|
36
|
+
export declare function urlAuthority(url: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Default CSRF defense: unsafe methods must present an Origin or Referer
|
|
39
|
+
* whose authority matches the request Host. Browsers always attach Origin
|
|
40
|
+
* to cross-site POSTs, so mismatches are blocked; non-browser clients that
|
|
41
|
+
* send no Origin/Referer are allowed (they cannot be CSRF'd).
|
|
42
|
+
* Throws a 403-status error on mismatch.
|
|
43
|
+
*/
|
|
44
|
+
export declare function assertSameOrigin(request: Record<string, unknown>): void;
|
|
21
45
|
export declare function signCookie(name: string, value: string, host?: string): Promise<string>;
|
|
22
46
|
export declare function unsignCookie(name: string, signedValue: string, host?: string): Promise<string | null>;
|
|
23
47
|
export interface CookieOptions {
|
|
@@ -63,6 +87,8 @@ export interface RateLimitOptions {
|
|
|
63
87
|
windowMs?: number;
|
|
64
88
|
max?: number;
|
|
65
89
|
cleanupIntervalMs?: number;
|
|
90
|
+
/** Only honor proxy headers (x-forwarded-for etc.) when explicitly trusted. */
|
|
91
|
+
trustProxy?: boolean;
|
|
66
92
|
}
|
|
67
93
|
export declare function createRateLimiter(options?: RateLimitOptions): {
|
|
68
94
|
check: (key: string) => boolean;
|
|
@@ -74,6 +100,11 @@ export declare function createRateLimiter(options?: RateLimitOptions): {
|
|
|
74
100
|
};
|
|
75
101
|
middleware: (request: Record<string, unknown>, response?: Record<string, unknown>) => boolean;
|
|
76
102
|
};
|
|
103
|
+
/**
|
|
104
|
+
* Resolves the client IP. Proxy headers (`x-forwarded-for`, `x-real-ip`) are
|
|
105
|
+
* only honored when `trustProxy` is explicitly enabled — otherwise any
|
|
106
|
+
* client could spoof its identity (and defeat rate limiting) with a header.
|
|
107
|
+
*/
|
|
77
108
|
export declare function getClientIp(request: Record<string, unknown> | undefined, trustProxy?: boolean | string): string;
|
|
78
109
|
export declare function getClientProtocol(request: Record<string, unknown> | undefined, trustProxy?: boolean | string): string;
|
|
79
110
|
export declare function applyTrustProxy(ctx: Record<string, unknown>, trustProxy: boolean | string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-utils.d.ts","sourceRoot":"","sources":["../src/server-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAapD,eAAO,IAAI,YAAY,SAAQ,CAAC;AAChC,eAAO,IAAI,OAAO,QAAI,CAAC;AACvB,eAAO,IAAI,eAAe,SAAQ,CAAC;AACnC,eAAO,IAAI,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAW,CAAC;AAEzD,wBAAgB,aAAa,CAAC,OAAO,UAAQ,GAAG,IAAI,CAInD;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAE9C;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,IAAI,CAEnE;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAEjD;AAED,wBAAgB,iBAAiB,IAAI,OAAO,CAI3C;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA6CjD;AAuCD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAMhD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO9C;
|
|
1
|
+
{"version":3,"file":"server-utils.d.ts","sourceRoot":"","sources":["../src/server-utils.ts"],"names":[],"mappings":"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"}
|