kempo 4.2.24 → 4.2.26
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/README.md +14 -0
- package/bin/cli.js +10 -4
- package/dist/admin/init.js +1 -1
- package/dist/admin/init.js.map +1 -1
- package/dist/kempo/vendor/lexical/chunks/chunk-3XOIGVQI.js +3 -0
- package/dist/kempo/vendor/lexical/chunks/chunk-ERAFEOAX.js +2 -0
- package/dist/kempo/vendor/lexical/chunks/chunk-FYATIV3K.js +3 -0
- package/dist/kempo/vendor/lexical/chunks/chunk-LMH5Y7M4.js +1 -0
- package/dist/kempo/vendor/lexical/chunks/chunk-PZQA5MC4.js +1 -0
- package/dist/kempo/vendor/lexical/chunks/chunk-ZMJQPFLE.js +11 -0
- package/dist/kempo/vendor/lexical/lexical.js +1 -0
- package/dist/kempo/vendor/lexical/lexical__code.js +12 -0
- package/dist/kempo/vendor/lexical/lexical__history.js +1 -0
- package/dist/kempo/vendor/lexical/lexical__html.js +1 -0
- package/dist/kempo/vendor/lexical/lexical__link.js +1 -0
- package/dist/kempo/vendor/lexical/lexical__list.js +1 -0
- package/dist/kempo/vendor/lexical/lexical__rich-text.js +1 -0
- package/dist/kempo/vendor/lexical/lexical__selection.js +1 -0
- package/dist/kempo/vendor/lexical/lexical__table.js +2 -0
- package/dist/kempo/vendor/lexical/manifest.json +14 -0
- package/middleware/kempo.js +77 -0
- package/package.json +12 -2
- package/server/utils/lexical/packages.js +28 -0
package/middleware/kempo.js
CHANGED
|
@@ -4,6 +4,7 @@ import { pathToFileURL, fileURLToPath } from 'url';
|
|
|
4
4
|
import { renderExternalPage } from 'kempo-server/templating';
|
|
5
5
|
import getSession from '../server/utils/auth/getSession.js';
|
|
6
6
|
import currentUserHasPermission from '../server/utils/permissions/currentUserHasPermission.js';
|
|
7
|
+
import { LEXICAL_BASE, bundleFileName as lexicalBundleFileName } from '../server/utils/lexical/packages.js';
|
|
7
8
|
import { getEnabledExtensions } from '../server/utils/extensions/scopeCache.js';
|
|
8
9
|
import triggerHook from '../server/utils/hooks/triggerHook.js';
|
|
9
10
|
import { ADMIN_GLOBALS_DIR } from '../server/utils/admin-global-content/helpers.js';
|
|
@@ -34,6 +35,24 @@ const NODE_MODULES = join(process.cwd(), 'node_modules');
|
|
|
34
35
|
*/
|
|
35
36
|
const DEFAULT_ADMIN_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', 'dist', 'admin');
|
|
36
37
|
|
|
38
|
+
/*
|
|
39
|
+
Where the lexical bundles live and the URL they are served at. Must stay in step with
|
|
40
|
+
window.kempo.lexicalUrl in src/admin/init.js, which is what kempo-ui reads.
|
|
41
|
+
*/
|
|
42
|
+
const LEXICAL_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', 'dist', 'kempo', 'vendor', 'lexical');
|
|
43
|
+
|
|
44
|
+
let lexicalVersionCache = null;
|
|
45
|
+
const lexicalVersion = async () => {
|
|
46
|
+
if(lexicalVersionCache) return lexicalVersionCache;
|
|
47
|
+
try {
|
|
48
|
+
const manifest = JSON.parse(await readFile(join(LEXICAL_DIR, 'manifest.json'), 'utf8'));
|
|
49
|
+
lexicalVersionCache = manifest.version;
|
|
50
|
+
} catch {
|
|
51
|
+
lexicalVersionCache = 'unknown';
|
|
52
|
+
}
|
|
53
|
+
return lexicalVersionCache;
|
|
54
|
+
};
|
|
55
|
+
|
|
37
56
|
const resolveAdminRoot = adminRoot => {
|
|
38
57
|
if(!adminRoot) return DEFAULT_ADMIN_ROOT;
|
|
39
58
|
return isAbsolute(adminRoot) ? adminRoot : resolve(process.cwd(), adminRoot);
|
|
@@ -157,6 +176,64 @@ export default config => {
|
|
|
157
176
|
const { path } = request;
|
|
158
177
|
const url = request.url.split('?')[0];
|
|
159
178
|
|
|
179
|
+
/*
|
|
180
|
+
Locally hosted lexical for the admin's WYSIWYG.
|
|
181
|
+
|
|
182
|
+
kempo-ui's HtmlEditor requests `${window.kempo.lexicalUrl}/${pkg}@${version}`, an esm.sh-shaped
|
|
183
|
+
URL with no file extension, so it cannot be served by a static route: the package name
|
|
184
|
+
contains a slash and the response needs a JavaScript content type to be accepted as a module.
|
|
185
|
+
This maps that shape onto the bundles the build produced.
|
|
186
|
+
|
|
187
|
+
Served before the auth gate on purpose. These are public third-party assets, and requiring a
|
|
188
|
+
session would make the editor fail for exactly the reader who is allowed to see it.
|
|
189
|
+
*/
|
|
190
|
+
if(url.startsWith(`${LEXICAL_BASE}/`)){
|
|
191
|
+
const requested = decodeURIComponent(url.slice(LEXICAL_BASE.length + 1));
|
|
192
|
+
const match = requested.match(/^(.+)@([^@]+)$/);
|
|
193
|
+
|
|
194
|
+
/*
|
|
195
|
+
Shared chunks. Splitting puts lexical's core in a chunk that the package entries import by
|
|
196
|
+
relative path, so those requests arrive here as ordinary filenames with no version suffix.
|
|
197
|
+
*/
|
|
198
|
+
let file = null;
|
|
199
|
+
if(match){
|
|
200
|
+
file = join(LEXICAL_DIR, lexicalBundleFileName(match[1]));
|
|
201
|
+
if(match[2] !== await lexicalVersion()){
|
|
202
|
+
console.warn(`[kempo] lexical ${match[2]} requested but ${await lexicalVersion()} is bundled — rebuild kempo, or kempo-ui's LEXICAL_VERSION has moved`);
|
|
203
|
+
}
|
|
204
|
+
} else if(/^[\w./-]+\.js$/.test(requested) && !requested.includes('..')){
|
|
205
|
+
file = join(LEXICAL_DIR, requested);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if(file){
|
|
209
|
+
try {
|
|
210
|
+
const info = await stat(file);
|
|
211
|
+
/*
|
|
212
|
+
Revalidated rather than immutable. The URL carries lexical's version but not kempo's, so
|
|
213
|
+
rebuilding kempo changes what lives at the same URL, and caching that for a year would
|
|
214
|
+
strand every client on a stale bundle. A 304 costs almost nothing.
|
|
215
|
+
*/
|
|
216
|
+
const etag = `"${info.size}-${Math.floor(info.mtimeMs)}"`;
|
|
217
|
+
if(request.headers['if-none-match'] === etag){
|
|
218
|
+
response.writeHead(304, { ETag: etag, 'Cache-Control': 'public, must-revalidate' });
|
|
219
|
+
response.end();
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
response.writeHead(200, {
|
|
223
|
+
'Content-Type': 'application/javascript; charset=utf-8',
|
|
224
|
+
'Cache-Control': 'public, must-revalidate',
|
|
225
|
+
ETag: etag,
|
|
226
|
+
});
|
|
227
|
+
response.end(await readFile(file));
|
|
228
|
+
return;
|
|
229
|
+
} catch {
|
|
230
|
+
response.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
231
|
+
response.end(`No bundled lexical asset "${requested}". Run \`npm run build\` in kempo.`);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
160
237
|
/*
|
|
161
238
|
Auth: protect /account and /admin routes
|
|
162
239
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kempo",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.26",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A fullstack CMS framework for creating websites with user authentication",
|
|
6
6
|
"scripts": {
|
|
@@ -37,11 +37,20 @@
|
|
|
37
37
|
"scripts/init-db.js"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
+
"@lexical/code": "0.43.0",
|
|
41
|
+
"@lexical/history": "0.43.0",
|
|
42
|
+
"@lexical/html": "0.43.0",
|
|
43
|
+
"@lexical/link": "0.43.0",
|
|
44
|
+
"@lexical/list": "0.43.0",
|
|
45
|
+
"@lexical/rich-text": "0.43.0",
|
|
46
|
+
"@lexical/selection": "0.43.0",
|
|
47
|
+
"@lexical/table": "0.43.0",
|
|
40
48
|
"bcrypt": "^6.0.0",
|
|
41
49
|
"dotenv": "^17.2.3",
|
|
42
50
|
"drizzle-kit": "^0.31.7",
|
|
43
51
|
"drizzle-orm": "^0.45.2",
|
|
44
|
-
"inquirer": "^
|
|
52
|
+
"inquirer": "^14.0.2",
|
|
53
|
+
"lexical": "0.43.0",
|
|
45
54
|
"monaco-editor": "^0.56.0",
|
|
46
55
|
"postgres": "^3.4.7",
|
|
47
56
|
"resend": "^6.5.2"
|
|
@@ -52,6 +61,7 @@
|
|
|
52
61
|
"kempo-ui": ">=0.4.39"
|
|
53
62
|
},
|
|
54
63
|
"devDependencies": {
|
|
64
|
+
"esbuild": "^0.28.2",
|
|
55
65
|
"html-minifier-terser": "^7.2.0",
|
|
56
66
|
"kempo-testing-framework": "^1.4.6",
|
|
57
67
|
"terser": "^5.46.2"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The lexical packages kempo-ui's HtmlEditor imports, and how each one's browser bundle is named on
|
|
3
|
+
disk. Shared by the build (which produces the bundles) and the middleware (which serves them), so
|
|
4
|
+
the two cannot drift.
|
|
5
|
+
|
|
6
|
+
This lives under server/utils rather than scripts/ because scripts/ is not published — a
|
|
7
|
+
middleware importing from there would resolve here but be missing for every consumer.
|
|
8
|
+
*/
|
|
9
|
+
/*
|
|
10
|
+
Where the bundles are served. The build stamps this into chunk references and the middleware
|
|
11
|
+
answers on it, so the two must agree.
|
|
12
|
+
*/
|
|
13
|
+
export const LEXICAL_BASE = '/kempo/vendor/lexical';
|
|
14
|
+
|
|
15
|
+
export const LEXICAL_PACKAGES = [
|
|
16
|
+
'lexical',
|
|
17
|
+
'@lexical/rich-text',
|
|
18
|
+
'@lexical/html',
|
|
19
|
+
'@lexical/history',
|
|
20
|
+
'@lexical/list',
|
|
21
|
+
'@lexical/link',
|
|
22
|
+
'@lexical/selection',
|
|
23
|
+
'@lexical/table',
|
|
24
|
+
'@lexical/code',
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
// "@lexical/rich-text" is not a usable filename, so the scope marker and slash are flattened
|
|
28
|
+
export const bundleFileName = pkg => `${pkg.replace('@', '').replace('/', '__')}.js`;
|