lastriko 0.1.0

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.
Files changed (101) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +58 -0
  3. package/dist/__tests__/integration/ws-flow.integration.test.d.ts +1 -0
  4. package/dist/__tests__/integration/ws-flow.integration.test.js +249 -0
  5. package/dist/__tests__/integration/ws-flow.integration.test.js.map +1 -0
  6. package/dist/__tests__/integration/ws-flow.test.d.ts +1 -0
  7. package/dist/__tests__/integration/ws-flow.test.js +70 -0
  8. package/dist/__tests__/integration/ws-flow.test.js.map +1 -0
  9. package/dist/client/events.d.ts +6 -0
  10. package/dist/client/events.js +114 -0
  11. package/dist/client/events.js.map +1 -0
  12. package/dist/client/index.d.ts +2 -0
  13. package/dist/client/index.js +5 -0
  14. package/dist/client/index.js.map +1 -0
  15. package/dist/client/swap.d.ts +4 -0
  16. package/dist/client/swap.js +47 -0
  17. package/dist/client/swap.js.map +1 -0
  18. package/dist/client/ws.d.ts +11 -0
  19. package/dist/client/ws.js +111 -0
  20. package/dist/client/ws.js.map +1 -0
  21. package/dist/components/context.d.ts +51 -0
  22. package/dist/components/context.js +595 -0
  23. package/dist/components/context.js.map +1 -0
  24. package/dist/components/context.test.d.ts +1 -0
  25. package/dist/components/context.test.js +69 -0
  26. package/dist/components/context.test.js.map +1 -0
  27. package/dist/components/id.d.ts +3 -0
  28. package/dist/components/id.js +9 -0
  29. package/dist/components/id.js.map +1 -0
  30. package/dist/components/registry.d.ts +2 -0
  31. package/dist/components/registry.js +97 -0
  32. package/dist/components/registry.js.map +1 -0
  33. package/dist/components/types.d.ts +390 -0
  34. package/dist/components/types.js +2 -0
  35. package/dist/components/types.js.map +1 -0
  36. package/dist/engine/executor.d.ts +20 -0
  37. package/dist/engine/executor.js +84 -0
  38. package/dist/engine/executor.js.map +1 -0
  39. package/dist/engine/executor.test.d.ts +1 -0
  40. package/dist/engine/executor.test.js +79 -0
  41. package/dist/engine/executor.test.js.map +1 -0
  42. package/dist/engine/index.d.ts +6 -0
  43. package/dist/engine/index.js +7 -0
  44. package/dist/engine/index.js.map +1 -0
  45. package/dist/engine/lifecycle.d.ts +16 -0
  46. package/dist/engine/lifecycle.js +44 -0
  47. package/dist/engine/lifecycle.js.map +1 -0
  48. package/dist/engine/lifecycle.test.d.ts +1 -0
  49. package/dist/engine/lifecycle.test.js +15 -0
  50. package/dist/engine/lifecycle.test.js.map +1 -0
  51. package/dist/engine/messages.d.ts +95 -0
  52. package/dist/engine/messages.js +21 -0
  53. package/dist/engine/messages.js.map +1 -0
  54. package/dist/engine/renderer.components.test.d.ts +1 -0
  55. package/dist/engine/renderer.components.test.js +302 -0
  56. package/dist/engine/renderer.components.test.js.map +1 -0
  57. package/dist/engine/renderer.d.ts +4 -0
  58. package/dist/engine/renderer.js +408 -0
  59. package/dist/engine/renderer.js.map +1 -0
  60. package/dist/engine/renderer.test.d.ts +1 -0
  61. package/dist/engine/renderer.test.js +80 -0
  62. package/dist/engine/renderer.test.js.map +1 -0
  63. package/dist/engine/server.d.ts +43 -0
  64. package/dist/engine/server.js +402 -0
  65. package/dist/engine/server.js.map +1 -0
  66. package/dist/engine/server.test.d.ts +1 -0
  67. package/dist/engine/server.test.js +149 -0
  68. package/dist/engine/server.test.js.map +1 -0
  69. package/dist/engine/shell.d.ts +7 -0
  70. package/dist/engine/shell.js +25 -0
  71. package/dist/engine/shell.js.map +1 -0
  72. package/dist/engine/theme-path.d.ts +11 -0
  73. package/dist/engine/theme-path.js +32 -0
  74. package/dist/engine/theme-path.js.map +1 -0
  75. package/dist/engine/theme-path.test.d.ts +1 -0
  76. package/dist/engine/theme-path.test.js +52 -0
  77. package/dist/engine/theme-path.test.js.map +1 -0
  78. package/dist/engine/watcher.d.ts +8 -0
  79. package/dist/engine/watcher.js +27 -0
  80. package/dist/engine/watcher.js.map +1 -0
  81. package/dist/engine/websocket.d.ts +24 -0
  82. package/dist/engine/websocket.hub.test.d.ts +1 -0
  83. package/dist/engine/websocket.hub.test.js +75 -0
  84. package/dist/engine/websocket.hub.test.js.map +1 -0
  85. package/dist/engine/websocket.js +119 -0
  86. package/dist/engine/websocket.js.map +1 -0
  87. package/dist/index.d.ts +21 -0
  88. package/dist/index.js +26 -0
  89. package/dist/index.js.map +1 -0
  90. package/dist/plugins/registry.d.ts +11 -0
  91. package/dist/plugins/registry.js +61 -0
  92. package/dist/plugins/registry.js.map +1 -0
  93. package/dist/plugins/registry.test.d.ts +1 -0
  94. package/dist/plugins/registry.test.js +44 -0
  95. package/dist/plugins/registry.test.js.map +1 -0
  96. package/dist/plugins/types.d.ts +30 -0
  97. package/dist/plugins/types.js +2 -0
  98. package/dist/plugins/types.js.map +1 -0
  99. package/dist/style.css +134 -0
  100. package/dist/theme/lastriko.css +134 -0
  101. package/package.json +60 -0
@@ -0,0 +1,402 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
3
+ import { createServer as createHttpServer, } from 'node:http';
4
+ import { dirname, join } from 'node:path';
5
+ import process from 'node:process';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { tmpdir } from 'node:os';
8
+ import { WebSocketServer } from 'ws';
9
+ import { createHtmlShell } from './shell';
10
+ import { resolveThemeCssPath } from './theme-path';
11
+ import { createWebSocketHub } from './websocket';
12
+ const DEFAULT_PORT = 3500;
13
+ /** Max extra ports to try after the first (inclusive of first = maxAttempts tries). */
14
+ const PORT_HOP_MAX_ATTEMPTS = 64;
15
+ function resolveClientRootPath(cwd) {
16
+ const monorepoDist = join(cwd, 'packages/core/dist/client');
17
+ if (existsSync(monorepoDist))
18
+ return monorepoDist;
19
+ const here = dirname(fileURLToPath(import.meta.url));
20
+ const packageDist = join(here, '../client');
21
+ if (existsSync(packageDist))
22
+ return packageDist;
23
+ return null;
24
+ }
25
+ function resolveClientModulePath(clientRootPath, requestUrl) {
26
+ const normalizedUrl = requestUrl === '/client.js'
27
+ ? '/client/index.js'
28
+ : requestUrl;
29
+ if (!normalizedUrl.startsWith('/client/')) {
30
+ return null;
31
+ }
32
+ const rel = normalizedUrl.slice('/client/'.length);
33
+ if (!rel || rel.includes('..')) {
34
+ return null;
35
+ }
36
+ const normalized = rel.endsWith('.js') ? rel : `${rel}.js`;
37
+ const abs = join(clientRootPath, normalized);
38
+ return existsSync(abs) ? abs : null;
39
+ }
40
+ function normalizeIncomingWsData(data) {
41
+ if (typeof data === 'string')
42
+ return data;
43
+ if (data instanceof ArrayBuffer)
44
+ return Buffer.from(data).toString('utf8');
45
+ if (Array.isArray(data))
46
+ return Buffer.concat(data).toString('utf8');
47
+ if (Buffer.isBuffer(data))
48
+ return data.toString('utf8');
49
+ return null;
50
+ }
51
+ function rewriteClientEntryImports(source) {
52
+ return source
53
+ .replaceAll('from \'./', 'from \'/client/')
54
+ .replaceAll('from "./', 'from "/client/')
55
+ .replaceAll('import \'./', 'import \'/client/')
56
+ .replaceAll('import "./', 'import "/client/');
57
+ }
58
+ function parseBoundary(contentType) {
59
+ if (!contentType) {
60
+ return null;
61
+ }
62
+ const match = /boundary=([^;]+)/i.exec(contentType);
63
+ return match ? match[1].trim() : null;
64
+ }
65
+ function parseMultipartBody(buffer, boundary) {
66
+ const marker = `--${boundary}`;
67
+ const source = buffer.toString('binary');
68
+ const segments = source.split(marker);
69
+ const files = [];
70
+ for (const segment of segments) {
71
+ const part = segment.trim();
72
+ if (!part || part === '--') {
73
+ continue;
74
+ }
75
+ const sepIdx = part.indexOf('\r\n\r\n');
76
+ if (sepIdx < 0) {
77
+ continue;
78
+ }
79
+ const rawHeaders = part.slice(0, sepIdx);
80
+ const bodyBinary = part.slice(sepIdx + 4).replace(/\r\n--$/, '').replace(/\r\n$/, '');
81
+ const filenameMatch = /filename="([^"]+)"/i.exec(rawHeaders);
82
+ if (!filenameMatch) {
83
+ continue;
84
+ }
85
+ const typeMatch = /content-type:\s*([^\r\n]+)/i.exec(rawHeaders);
86
+ const filename = filenameMatch[1];
87
+ const contentType = typeMatch ? typeMatch[1].trim() : 'application/octet-stream';
88
+ const content = Buffer.from(bodyBinary, 'binary');
89
+ files.push({ filename, contentType, content });
90
+ }
91
+ return files;
92
+ }
93
+ function collectBody(req) {
94
+ return new Promise((resolve, reject) => {
95
+ const chunks = [];
96
+ req.on('data', (chunk) => chunks.push(chunk));
97
+ req.on('end', () => resolve(Buffer.concat(chunks)));
98
+ req.on('error', reject);
99
+ });
100
+ }
101
+ async function listenWithPortHop(server, initialPort, host) {
102
+ if (initialPort === 0) {
103
+ await new Promise((resolveListen, rejectListen) => {
104
+ const onError = (err) => {
105
+ server.off('error', onError);
106
+ rejectListen(err);
107
+ };
108
+ server.once('error', onError);
109
+ server.listen(0, host, () => {
110
+ server.off('error', onError);
111
+ resolveListen();
112
+ });
113
+ });
114
+ const addr = server.address();
115
+ if (addr && typeof addr === 'object')
116
+ return addr.port;
117
+ throw new Error('Could not determine ephemeral listen port');
118
+ }
119
+ const start = Math.max(1, Math.min(initialPort, 65535));
120
+ const end = Math.min(start + PORT_HOP_MAX_ATTEMPTS - 1, 65535);
121
+ async function tryPort(port) {
122
+ await new Promise((resolveListen, rejectListen) => {
123
+ const onError = (err) => {
124
+ server.off('error', onError);
125
+ rejectListen(err);
126
+ };
127
+ server.once('error', onError);
128
+ server.listen(port, host, () => {
129
+ server.off('error', onError);
130
+ resolveListen();
131
+ });
132
+ });
133
+ return port;
134
+ }
135
+ return await (async function hop(from) {
136
+ try {
137
+ return await tryPort(from);
138
+ }
139
+ catch (err) {
140
+ const e = err;
141
+ if (e.code !== 'EADDRINUSE' || from >= end) {
142
+ throw err;
143
+ }
144
+ if (from < end && process.env.NODE_ENV !== 'test') {
145
+ console.error(`[lastriko] Port ${from} is in use, trying ${from + 1}…`);
146
+ }
147
+ return hop(from + 1);
148
+ }
149
+ })(start);
150
+ }
151
+ function createHttpHandler(opts) {
152
+ return async (req, res) => {
153
+ try {
154
+ if (!req.url) {
155
+ res.statusCode = 404;
156
+ res.end('Not Found');
157
+ return;
158
+ }
159
+ const pathname = req.url.split('?')[0];
160
+ if (pathname === '/') {
161
+ opts.onRoot?.();
162
+ const html = createHtmlShell({
163
+ title: opts.title,
164
+ initialTheme: opts.getTheme(),
165
+ includeToolbar: opts.toolbar,
166
+ bodyHtml: '',
167
+ clientScriptPath: opts.clientPath ?? '/client.js',
168
+ });
169
+ res.statusCode = 200;
170
+ res.setHeader('content-type', 'text/html; charset=utf-8');
171
+ res.end(html);
172
+ return;
173
+ }
174
+ if (pathname === '/style.css') {
175
+ const cssPath = opts.themeCssPath;
176
+ if (!cssPath) {
177
+ res.statusCode = 500;
178
+ res.setHeader('content-type', 'text/plain; charset=utf-8');
179
+ res.end('Lastriko theme CSS not found. Set LASTRIKO_THEME_CSS to an existing file, or install a published lastriko package with dist/theme/lastriko.css.');
180
+ return;
181
+ }
182
+ try {
183
+ const css = readFileSync(cssPath, 'utf8');
184
+ res.statusCode = 200;
185
+ res.setHeader('content-type', 'text/css; charset=utf-8');
186
+ res.end(css);
187
+ }
188
+ catch (err) {
189
+ const msg = err instanceof Error ? err.message : String(err);
190
+ res.statusCode = 500;
191
+ res.setHeader('content-type', 'text/plain; charset=utf-8');
192
+ res.end(`Failed to read theme CSS: ${msg}`);
193
+ }
194
+ return;
195
+ }
196
+ if (pathname === '/upload' && req.method === 'POST') {
197
+ const boundary = parseBoundary(req.headers['content-type']);
198
+ if (!boundary) {
199
+ res.statusCode = 400;
200
+ res.setHeader('content-type', 'application/json; charset=utf-8');
201
+ res.end(JSON.stringify({ error: 'Missing multipart boundary' }));
202
+ return;
203
+ }
204
+ const body = await collectBody(req);
205
+ const parts = parseMultipartBody(body, boundary);
206
+ if (parts.length === 0) {
207
+ res.statusCode = 400;
208
+ res.setHeader('content-type', 'application/json; charset=utf-8');
209
+ res.end(JSON.stringify({ error: 'No file part' }));
210
+ return;
211
+ }
212
+ const scopeDirName = new URL(`http://localhost${req.url}`).searchParams.get('connectionId')
213
+ ?? req.headers['x-lastriko-connection']
214
+ ?? req.headers['x-lastriko-connection-id']
215
+ ?? 'shared';
216
+ const safeScopeDir = String(scopeDirName).replaceAll(/[^\w.-]/g, '_').slice(0, 120) || 'shared';
217
+ const uploadDir = join(opts.uploadDirRoot, safeScopeDir);
218
+ mkdirSync(uploadDir, { recursive: true });
219
+ const saved = parts.map((part, index) => {
220
+ const name = `${Date.now()}-${index}-${part.filename}`;
221
+ const path = join(uploadDir, name);
222
+ writeFileSync(path, part.content);
223
+ return {
224
+ name: part.filename,
225
+ path,
226
+ size: part.content.byteLength,
227
+ type: part.contentType,
228
+ lastModified: Date.now(),
229
+ };
230
+ });
231
+ res.statusCode = 200;
232
+ res.setHeader('content-type', 'application/json; charset=utf-8');
233
+ res.end(JSON.stringify(saved.length === 1 ? { file: saved[0] } : saved));
234
+ return;
235
+ }
236
+ if (pathname === '/client.js' || pathname.startsWith('/client/')) {
237
+ const clientRootPath = opts.clientRootPath;
238
+ if (!clientRootPath) {
239
+ res.statusCode = 500;
240
+ res.setHeader('content-type', 'text/plain; charset=utf-8');
241
+ res.end('Lastriko client bundle not found. Build packages/core so dist/client exists.');
242
+ return;
243
+ }
244
+ if (pathname === '/client.js') {
245
+ const entryPath = resolveClientModulePath(clientRootPath, '/client/index.js');
246
+ if (!entryPath) {
247
+ res.statusCode = 500;
248
+ res.setHeader('content-type', 'text/plain; charset=utf-8');
249
+ res.end('Lastriko client entry not found at dist/client/index.js.');
250
+ return;
251
+ }
252
+ try {
253
+ const entryJs = readFileSync(entryPath, 'utf8');
254
+ const rewritten = rewriteClientEntryImports(entryJs);
255
+ res.statusCode = 200;
256
+ res.setHeader('content-type', 'application/javascript; charset=utf-8');
257
+ res.end(rewritten);
258
+ }
259
+ catch (err) {
260
+ const msg = err instanceof Error ? err.message : String(err);
261
+ res.statusCode = 500;
262
+ res.setHeader('content-type', 'text/plain; charset=utf-8');
263
+ res.end(`Failed to read client entry bundle: ${msg}`);
264
+ }
265
+ return;
266
+ }
267
+ const modulePath = resolveClientModulePath(clientRootPath, pathname);
268
+ if (!modulePath) {
269
+ res.statusCode = 404;
270
+ res.setHeader('content-type', 'text/plain; charset=utf-8');
271
+ res.end('Not Found');
272
+ return;
273
+ }
274
+ try {
275
+ const js = readFileSync(modulePath, 'utf8');
276
+ res.statusCode = 200;
277
+ res.setHeader('content-type', 'application/javascript; charset=utf-8');
278
+ res.end(js);
279
+ }
280
+ catch (err) {
281
+ const msg = err instanceof Error ? err.message : String(err);
282
+ res.statusCode = 500;
283
+ res.setHeader('content-type', 'text/plain; charset=utf-8');
284
+ res.end(`Failed to read client bundle: ${msg}`);
285
+ }
286
+ return;
287
+ }
288
+ res.statusCode = 404;
289
+ res.end('Not Found');
290
+ }
291
+ catch (err) {
292
+ const msg = err instanceof Error ? err.message : String(err);
293
+ if (!res.headersSent) {
294
+ res.statusCode = 500;
295
+ res.setHeader('content-type', 'text/plain; charset=utf-8');
296
+ res.end(`Internal Server Error: ${msg}`);
297
+ }
298
+ else {
299
+ res.destroy();
300
+ }
301
+ }
302
+ };
303
+ }
304
+ export async function startServer(definition, config = {}) {
305
+ const transport = { send: () => { } };
306
+ void transport;
307
+ void definition.callback;
308
+ const themeCssPath = resolveThemeCssPath(process.cwd());
309
+ const clientRootPath = resolveClientRootPath(process.cwd());
310
+ const wsHub = createWebSocketHub({
311
+ title: definition.title,
312
+ callback: definition.callback,
313
+ });
314
+ const server = createHttpServer(createHttpHandler({
315
+ title: definition.title,
316
+ toolbar: true,
317
+ getTheme: () => config.theme ?? 'light',
318
+ themeCssPath,
319
+ clientRootPath,
320
+ uploadDirRoot: join(tmpdir(), 'lastriko-uploads'),
321
+ }));
322
+ const wsServer = new WebSocketServer({ noServer: true });
323
+ wsServer.on('connection', (socket) => {
324
+ wsHub.addConnection(socket);
325
+ socket.on('message', (data) => {
326
+ const normalized = normalizeIncomingWsData(data);
327
+ if (normalized === null)
328
+ return;
329
+ wsHub.handleRawMessage(socket, normalized);
330
+ });
331
+ socket.on('close', () => {
332
+ wsHub.removeConnection(socket);
333
+ });
334
+ socket.on('error', () => {
335
+ wsHub.removeConnection(socket);
336
+ });
337
+ });
338
+ server.on('upgrade', (req, socket, head) => {
339
+ if (req.url !== '/ws') {
340
+ socket.destroy();
341
+ return;
342
+ }
343
+ wsServer.handleUpgrade(req, socket, head, (client) => {
344
+ wsServer.emit('connection', client, req);
345
+ });
346
+ });
347
+ const host = config.host ?? '127.0.0.1';
348
+ const boundPort = await listenWithPortHop(server, config.port ?? DEFAULT_PORT, host);
349
+ return {
350
+ port: boundPort,
351
+ host,
352
+ server,
353
+ stop: async () => {
354
+ await new Promise((resolveClose, rejectClose) => {
355
+ for (const client of wsServer.clients) {
356
+ client.close();
357
+ }
358
+ wsServer.close((err) => {
359
+ if (err)
360
+ rejectClose(err);
361
+ else
362
+ resolveClose();
363
+ });
364
+ });
365
+ await new Promise((resolveClose, rejectClose) => {
366
+ server.close((err) => {
367
+ if (err)
368
+ rejectClose(err);
369
+ else
370
+ resolveClose();
371
+ });
372
+ });
373
+ },
374
+ };
375
+ }
376
+ export const __internal = {
377
+ createHttpHandler,
378
+ };
379
+ export async function createServer(options) {
380
+ if (options.plugins) {
381
+ await options.plugins.setupAll();
382
+ }
383
+ const running = await startServer({
384
+ title: options.title,
385
+ callback: options.app,
386
+ plugins: options.plugins,
387
+ }, {
388
+ port: options.port,
389
+ host: options.host,
390
+ theme: options.theme,
391
+ });
392
+ return {
393
+ ...running,
394
+ stop: async () => {
395
+ if (options.plugins) {
396
+ await options.plugins.teardownAll();
397
+ }
398
+ await running.stop();
399
+ },
400
+ };
401
+ }
402
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/engine/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,149 @@
1
+ import { createServer as createHttpServer } from 'node:http';
2
+ import { dirname, join } from 'node:path';
3
+ import process from 'node:process';
4
+ import { tmpdir } from 'node:os';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { describe, expect, it } from 'vitest';
7
+ import { resolveThemeCssPath } from './theme-path';
8
+ import { __internal, startServer } from './server';
9
+ const uploadDirRoot = '/tmp/lastriko-upload-tests';
10
+ async function withHttpHandler(handler, fn) {
11
+ const srv = createHttpServer(handler);
12
+ await new Promise((resolve, reject) => {
13
+ srv.once('error', reject);
14
+ srv.listen(0, '127.0.0.1', () => resolve());
15
+ });
16
+ const addr = srv.address();
17
+ const port = typeof addr === 'object' && addr ? addr.port : null;
18
+ if (port == null) {
19
+ await new Promise((r, j) => srv.close((e) => (e ? j(e) : r())));
20
+ throw new Error('Could not bind test server');
21
+ }
22
+ const baseUrl = `http://127.0.0.1:${port}`;
23
+ try {
24
+ await fn(baseUrl);
25
+ }
26
+ finally {
27
+ await new Promise((resolve, reject) => {
28
+ srv.close((err) => (err ? reject(err) : resolve()));
29
+ });
30
+ }
31
+ }
32
+ describe('startServer port selection', () => {
33
+ it('defaults to port 3500 or hops when it is occupied', async () => {
34
+ const app = await startServer({ title: 'default-port', callback: () => { } }, { host: '127.0.0.1' });
35
+ try {
36
+ expect(app.port).toBeGreaterThanOrEqual(3500);
37
+ expect(app.port).toBeLessThanOrEqual(3563);
38
+ }
39
+ finally {
40
+ await app.stop();
41
+ }
42
+ });
43
+ it('uses the next port when the requested one is in use', async () => {
44
+ const blocker = createHttpServer();
45
+ await new Promise((resolve, reject) => {
46
+ blocker.once('error', reject);
47
+ blocker.listen(0, '127.0.0.1', () => resolve());
48
+ });
49
+ const addr = blocker.address();
50
+ const blocked = typeof addr === 'object' && addr ? addr.port : null;
51
+ expect(blocked).not.toBeNull();
52
+ const app = await startServer({ title: 'hop-test', callback: () => { } }, { port: blocked, host: '127.0.0.1' });
53
+ expect(app.port).toBe(blocked + 1);
54
+ await app.stop();
55
+ await new Promise((resolve, reject) => {
56
+ blocker.close((err) => (err ? reject(err) : resolve()));
57
+ });
58
+ });
59
+ });
60
+ describe('http handler resilience', () => {
61
+ const thisDir = dirname(fileURLToPath(import.meta.url));
62
+ const clientRoot = join(thisDir, '../../dist/client');
63
+ it('responds 500 for /style.css when theme file is missing without throwing', async () => {
64
+ const handler = __internal.createHttpHandler({
65
+ title: 't',
66
+ toolbar: false,
67
+ getTheme: () => 'light',
68
+ themeCssPath: null,
69
+ clientRootPath: null,
70
+ uploadDirRoot,
71
+ });
72
+ await withHttpHandler(handler, async (base) => {
73
+ const res = await fetch(`${base}/style.css`);
74
+ expect(res.status).toBe(500);
75
+ const text = await res.text();
76
+ expect(text).toContain('theme CSS');
77
+ });
78
+ });
79
+ it('serves /style.css when theme path is valid', async () => {
80
+ const cssPath = resolveThemeCssPath(process.cwd());
81
+ expect(cssPath).not.toBeNull();
82
+ const handler = __internal.createHttpHandler({
83
+ title: 't',
84
+ toolbar: false,
85
+ getTheme: () => 'light',
86
+ themeCssPath: cssPath,
87
+ clientRootPath: null,
88
+ uploadDirRoot,
89
+ });
90
+ await withHttpHandler(handler, async (base) => {
91
+ const res = await fetch(`${base}/style.css`);
92
+ expect(res.status).toBe(200);
93
+ const text = await res.text();
94
+ expect(text).toContain(':root');
95
+ });
96
+ });
97
+ it('serves built /client/index.js from resolved client root', async () => {
98
+ const handler = __internal.createHttpHandler({
99
+ title: 't',
100
+ toolbar: false,
101
+ getTheme: () => 'light',
102
+ themeCssPath: resolveThemeCssPath(process.cwd()),
103
+ clientRootPath: clientRoot,
104
+ uploadDirRoot,
105
+ });
106
+ await withHttpHandler(handler, async (base) => {
107
+ const res = await fetch(`${base}/client/index.js`);
108
+ expect(res.status).toBe(200);
109
+ const js = await res.text();
110
+ expect(js).toContain('createWSManager');
111
+ });
112
+ });
113
+ it('returns 404 for missing client module', async () => {
114
+ const handler = __internal.createHttpHandler({
115
+ title: 't',
116
+ toolbar: false,
117
+ getTheme: () => 'light',
118
+ themeCssPath: resolveThemeCssPath(process.cwd()),
119
+ clientRootPath: clientRoot,
120
+ uploadDirRoot,
121
+ });
122
+ await withHttpHandler(handler, async (base) => {
123
+ const res = await fetch(`${base}/client/nope.js`);
124
+ expect(res.status).toBe(404);
125
+ });
126
+ });
127
+ it('accepts multipart upload and returns uploaded file metadata', async () => {
128
+ const handler = __internal.createHttpHandler({
129
+ title: 't',
130
+ toolbar: false,
131
+ getTheme: () => 'light',
132
+ themeCssPath: resolveThemeCssPath(process.cwd()),
133
+ clientRootPath: clientRoot,
134
+ uploadDirRoot: join(tmpdir(), 'lastriko-upload-test'),
135
+ });
136
+ await withHttpHandler(handler, async (base) => {
137
+ const form = new FormData();
138
+ form.append('file', new Blob(['hello world'], { type: 'text/plain' }), 'hello.txt');
139
+ const res = await fetch(`${base}/upload?connectionId=scope-test`, { method: 'POST', body: form });
140
+ expect(res.status).toBe(200);
141
+ const payload = await res.json();
142
+ expect(payload.file.name).toBe('hello.txt');
143
+ expect(payload.file.path).toContain('scope-test');
144
+ expect(payload.file.size).toBeGreaterThan(0);
145
+ expect(payload.file.type).toContain('text/plain');
146
+ });
147
+ });
148
+ });
149
+ //# sourceMappingURL=server.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.test.js","sourceRoot":"","sources":["../../src/engine/server.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare function createHtmlShell(input: {
2
+ title: string;
3
+ initialTheme: 'light' | 'dark';
4
+ includeToolbar: boolean;
5
+ bodyHtml: string;
6
+ clientScriptPath: string;
7
+ }): string;
@@ -0,0 +1,25 @@
1
+ import { escapeHtml } from './renderer';
2
+ export function createHtmlShell(input) {
3
+ const toolbar = input.includeToolbar
4
+ ? `<header class="lk-toolbar">
5
+ <div class="lk-brand">Lastriko</div>
6
+ <button id="lk-theme-toggle" class="lk-theme-toggle" type="button">Theme</button>
7
+ </header>`
8
+ : '';
9
+ return `<!doctype html>
10
+ <html data-theme="${input.initialTheme}">
11
+ <head>
12
+ <meta charset="utf-8" />
13
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
14
+ <title>${escapeHtml(input.title)}</title>
15
+ <link rel="stylesheet" href="/style.css" />
16
+ <script type="module" src="${escapeHtml(input.clientScriptPath)}"></script>
17
+ </head>
18
+ <body>
19
+ ${toolbar}
20
+ <main id="lk-root" class="lk-page">${input.bodyHtml}</main>
21
+ <div id="lk-toast-root" class="lk-toast-root"></div>
22
+ </body>
23
+ </html>`;
24
+ }
25
+ //# sourceMappingURL=shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/engine/shell.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,UAAU,eAAe,CAAC,KAM/B;IACC,MAAM,OAAO,GAAG,KAAK,CAAC,cAAc;QAClC,CAAC,CAAC;;;cAGQ;QACV,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;oBACW,KAAK,CAAC,YAAY;;;;aAIzB,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC;;iCAEH,UAAU,CAAC,KAAK,CAAC,gBAAgB,CAAC;;;MAG7D,OAAO;yCAC4B,KAAK,CAAC,QAAQ;;;QAG/C,CAAC;AACT,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Resolve `lastriko.css` for `GET /style.css`.
3
+ *
4
+ * Resolution order:
5
+ * 1. `LASTRIKO_THEME_CSS` — absolute or cwd-relative path (must exist).
6
+ * 2. `packages/core/src/theme/lastriko.css` under `process.cwd()` (monorepo dev from repo root).
7
+ * 3. `../theme/lastriko.css` next to this module (`src/engine` or `dist/engine` after build).
8
+ *
9
+ * Returns `null` if no readable file is found (caller should respond with 500, not throw).
10
+ */
11
+ export declare function resolveThemeCssPath(cwd: string): string | null;