@dokebi/template-inertia 0.4.2 → 0.4.4
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/AI.md +136 -23
- package/README.md +23 -7
- package/dist/boot-fallback.d.ts +61 -0
- package/dist/boot-fallback.js +131 -0
- package/dist/built-files.d.ts +9 -0
- package/dist/built-files.js +44 -0
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js +3 -1
- package/dist/i18n-store.d.ts +64 -0
- package/dist/i18n-store.js +368 -0
- package/dist/i18n.d.ts +216 -0
- package/dist/i18n.js +901 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +426 -63
- package/dist/options.d.ts +28 -0
- package/dist/options.js +97 -1
- package/dist/prune.d.ts +30 -0
- package/dist/prune.js +159 -0
- package/dist/responder.d.ts +14 -4
- package/dist/responder.js +70 -11
- package/dist/vite-deps.d.ts +76 -0
- package/dist/vite-deps.js +249 -0
- package/dist/vite-dev.d.ts +42 -4
- package/dist/vite-dev.js +162 -16
- package/dist/vite-files.d.ts +129 -0
- package/dist/vite-files.js +633 -0
- package/dist/vite-host.js +67 -10
- package/dist/vite-plugin.d.ts +69 -0
- package/dist/vite-plugin.js +173 -0
- package/dist/vite-watch.d.ts +44 -0
- package/dist/vite-watch.js +176 -0
- package/options.schema.json +127 -1
- package/package.json +38 -11
package/dist/vite-dev.js
CHANGED
|
@@ -13,11 +13,14 @@
|
|
|
13
13
|
import { randomBytes, timingSafeEqual } from 'node:crypto';
|
|
14
14
|
import { Agent, createServer as createHttpServer, request as httpRequest, } from 'node:http';
|
|
15
15
|
import { createRequire } from 'node:module';
|
|
16
|
-
import { createServer as createNetServer, } from 'node:net';
|
|
16
|
+
import { connect as connectNet, createServer as createNetServer, } from 'node:net';
|
|
17
17
|
import { dirname, join } from 'node:path';
|
|
18
18
|
import { pathToFileURL } from 'node:url';
|
|
19
19
|
import { diagnostics } from '@dokebi/core/libraries';
|
|
20
20
|
import { InertiaError } from './errors.js';
|
|
21
|
+
import { DependencyCache } from './vite-deps.js';
|
|
22
|
+
import { DEV_CLIENT_HEADER, dokebiDevFiles } from './vite-files.js';
|
|
23
|
+
import { ServedFiles, staleEventFilter } from './vite-watch.js';
|
|
21
24
|
const { describeError } = diagnostics;
|
|
22
25
|
export const viteDevRequested = () => process.env.DOKEBI_VITE_DEV === '1';
|
|
23
26
|
/**
|
|
@@ -47,6 +50,11 @@ const LOOPBACK_HOSTS = ['127.0.0.1', '::1'];
|
|
|
47
50
|
const PORT_ATTEMPTS = 8;
|
|
48
51
|
/** WebSocket subprotocols of Vite's client; other upgrades on the host port are closed. */
|
|
49
52
|
const VITE_PROTOCOLS = new Set(['vite-hmr', 'vite-ping']);
|
|
53
|
+
/** Whether an upgrade request is Vite's client (its HMR or ping socket), by its subprotocol. */
|
|
54
|
+
export function isViteUpgrade(request) {
|
|
55
|
+
const protocol = request.headers['sec-websocket-protocol'];
|
|
56
|
+
return typeof protocol === 'string' && VITE_PROTOCOLS.has(protocol.trim());
|
|
57
|
+
}
|
|
50
58
|
/** Hop-by-hop headers (RFC 9110 §7.6.1) that a forwarder does not pass on. */
|
|
51
59
|
const HOP_BY_HOP = [
|
|
52
60
|
'connection',
|
|
@@ -59,6 +67,8 @@ const HOP_BY_HOP = [
|
|
|
59
67
|
'transfer-encoding',
|
|
60
68
|
'upgrade',
|
|
61
69
|
];
|
|
70
|
+
/** Default of `devDepsRepairIntervalMs` (D-0128). */
|
|
71
|
+
export const DEFAULT_DEPS_REPAIR_INTERVAL_MS = 10_000;
|
|
62
72
|
function listenExclusive(host, port) {
|
|
63
73
|
return new Promise((resolve, reject) => {
|
|
64
74
|
const listener = createNetServer();
|
|
@@ -206,14 +216,22 @@ function findVite(root) {
|
|
|
206
216
|
* - WebSocket upgrades of Vite's client (HMR): Vite answers them and keeps its token check
|
|
207
217
|
* (cross-site WebSocket hijacking). @dokebi/websocket answers every upgrade on the app's port,
|
|
208
218
|
* so HMR cannot share that one.
|
|
219
|
+
*
|
|
220
|
+
* Watcher events for files whose state did not change since Vite read them (FSEvents delivering
|
|
221
|
+
* writes from before the start, late) are dropped before Vite sees them (`vite-watch.ts`).
|
|
222
|
+
*
|
|
223
|
+
* The pre-bundled dependencies (node_modules/.vite/deps) are compared with what Vite serves at
|
|
224
|
+
* the start, whenever an app process connects (every app restart) and after a 504; when the
|
|
225
|
+
* folder changed under Vite, the host re-optimizes (`vite-deps.ts`, D-0128).
|
|
209
226
|
*/
|
|
210
|
-
export async function startViteHost(logger, root = process.cwd()) {
|
|
227
|
+
export async function startViteHost(logger, root = process.cwd(), settings = {}) {
|
|
211
228
|
const begin = performance.now();
|
|
212
229
|
const entry = findVite(root);
|
|
213
230
|
const vite = (await import(pathToFileURL(entry).href));
|
|
214
231
|
const key = randomBytes(24).toString('base64url');
|
|
215
232
|
const expected = Buffer.from(key);
|
|
216
233
|
let started;
|
|
234
|
+
let deps;
|
|
217
235
|
const report = (operation) => (error) => logger.log('error', 'Vite dev host request failed', {
|
|
218
236
|
code: 'INERTIA_VITE_HOST_FAILED',
|
|
219
237
|
operation,
|
|
@@ -259,25 +277,48 @@ export async function startViteHost(logger, root = process.cwd()) {
|
|
|
259
277
|
return;
|
|
260
278
|
}
|
|
261
279
|
const path = String(request.url ?? '').split('?', 1)[0];
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
280
|
+
// D-0129: a browser request the app forwarded (it carries DEV_CLIENT_HEADER) never reaches
|
|
281
|
+
// the host-only paths: with the Vite base `/` they sit under the dev route, and the ping
|
|
282
|
+
// names the project root.
|
|
283
|
+
const browser = request.headers[DEV_CLIENT_HEADER] !== undefined;
|
|
284
|
+
const cache = deps;
|
|
285
|
+
if (path === HOST_PATH && !browser) {
|
|
286
|
+
// An app process connects (it started or restarted): it is answered once the pre-bundled
|
|
287
|
+
// dependencies match what Vite serves, so its pages get hashes that load (D-0128).
|
|
288
|
+
void (cache ? cache.ensure('client').catch(report('deps')) : Promise.resolve()).then(() => sendJson(response, 200, { root, pid: process.pid }));
|
|
289
|
+
}
|
|
290
|
+
else if (path === SSR_PATH && request.method === 'POST' && !browser)
|
|
265
291
|
void ssr(request, response, server);
|
|
266
|
-
else
|
|
292
|
+
else {
|
|
293
|
+
// Vite answers 504 for a pre-bundled file it cannot serve: if the folder changed under it,
|
|
294
|
+
// re-optimize so the next page load gets files that exist (D-0128).
|
|
295
|
+
if (cache)
|
|
296
|
+
response.once('finish', () => {
|
|
297
|
+
if (response.statusCode === 504)
|
|
298
|
+
void cache.ensure('outdated').catch(report('deps'));
|
|
299
|
+
});
|
|
267
300
|
server.middlewares(request, response, (error) => viteFallthrough(response, error, report('vite')));
|
|
301
|
+
}
|
|
268
302
|
});
|
|
303
|
+
// Events FSEvents still holds from before the start are no edits (D-0045 addendum 4).
|
|
304
|
+
const served = new ServedFiles(logger);
|
|
305
|
+
served.recordConfig(root);
|
|
269
306
|
try {
|
|
270
307
|
started = await vite.createServer({
|
|
271
308
|
root,
|
|
272
309
|
appType: 'custom',
|
|
310
|
+
// D-0129: the dev file policy for a vite.config without dokebiInertia() (which carries it).
|
|
311
|
+
plugins: [staleEventFilter(served, logger), dokebiDevFiles(logger)],
|
|
273
312
|
server: {
|
|
274
313
|
middlewareMode: true,
|
|
275
314
|
// Hosts are checked by the dev routes of the plugin (see index.ts) and HTTP needs the key,
|
|
276
315
|
// so Vite's host check is off. The HMR socket keeps Vite's token check.
|
|
277
316
|
allowedHosts: true,
|
|
278
|
-
// Vite 8 `server.ws`: Vite answers upgrades on `server
|
|
279
|
-
//
|
|
280
|
-
|
|
317
|
+
// Vite 8 `server.ws`: Vite answers upgrades on `server`. No `port`: its client then
|
|
318
|
+
// connects to the page's own origin and `<base>` (the port the page came from, a tunnel's
|
|
319
|
+
// 443 included), and the app forwards that upgrade here (ViteDev.upgrade). With a port
|
|
320
|
+
// it went to `<page host>:<this loopback port>`, which no tunnel or other device reaches.
|
|
321
|
+
ws: { server: endpoint.server },
|
|
281
322
|
},
|
|
282
323
|
});
|
|
283
324
|
}
|
|
@@ -287,6 +328,24 @@ export async function startViteHost(logger, root = process.cwd()) {
|
|
|
287
328
|
throw error;
|
|
288
329
|
}
|
|
289
330
|
const server = started;
|
|
331
|
+
const cache = new DependencyCache(logger, server, {
|
|
332
|
+
repairIntervalMs: settings.depsRepairIntervalMs ?? DEFAULT_DEPS_REPAIR_INTERVAL_MS,
|
|
333
|
+
});
|
|
334
|
+
try {
|
|
335
|
+
await cache.claim();
|
|
336
|
+
// A cache left broken by an earlier session would answer every dependency 504 (D-0128).
|
|
337
|
+
await cache.ensure('start');
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
try {
|
|
341
|
+
await server.close();
|
|
342
|
+
}
|
|
343
|
+
finally {
|
|
344
|
+
await endpoint.close();
|
|
345
|
+
}
|
|
346
|
+
throw error;
|
|
347
|
+
}
|
|
348
|
+
deps = cache;
|
|
290
349
|
logger.log('debug', 'Vite dev host started', {
|
|
291
350
|
port: endpoint.port,
|
|
292
351
|
hosts: endpoint.hosts,
|
|
@@ -304,7 +363,12 @@ export async function startViteHost(logger, root = process.cwd()) {
|
|
|
304
363
|
await server.close();
|
|
305
364
|
}
|
|
306
365
|
finally {
|
|
307
|
-
|
|
366
|
+
try {
|
|
367
|
+
await cache.release();
|
|
368
|
+
}
|
|
369
|
+
finally {
|
|
370
|
+
await endpoint.close();
|
|
371
|
+
}
|
|
308
372
|
}
|
|
309
373
|
})()),
|
|
310
374
|
};
|
|
@@ -339,6 +403,21 @@ function hostUrl(url) {
|
|
|
339
403
|
throw new InertiaError('VITE_DEV_UNAVAILABLE', 'DOKEBI_VITE_DEV_URL must be http://127.0.0.1:<port> (a loopback address and port)');
|
|
340
404
|
return parsed;
|
|
341
405
|
}
|
|
406
|
+
/**
|
|
407
|
+
* Destroys a request to the host without leaving a listener on a pooled connection. Node 24.15
|
|
408
|
+
* (`_http_client` onSocket, onSocketNT) adds the connection's 'error' listener when the agent
|
|
409
|
+
* hands it to a request and, when that request was destroyed while it waited in the agent's
|
|
410
|
+
* queue, puts the connection back in the pool with the listener still on it: one more per such
|
|
411
|
+
* request for as long as the keep-alive connection lives (audit r6: MaxListenersExceededWarning
|
|
412
|
+
* after renders cut at their deadline). A request without its connection yet is destroyed once
|
|
413
|
+
* it has one, which closes that connection instead.
|
|
414
|
+
*/
|
|
415
|
+
export function destroyRequest(outgoing, error) {
|
|
416
|
+
if (outgoing.socket || outgoing.destroyed)
|
|
417
|
+
outgoing.destroy(error);
|
|
418
|
+
else
|
|
419
|
+
outgoing.once('socket', () => outgoing.destroy(error));
|
|
420
|
+
}
|
|
342
421
|
/**
|
|
343
422
|
* Connects this process to a Vite host and checks that it answers with the key. Requests share
|
|
344
423
|
* keep-alive connections; `close` ends them (the host keeps running).
|
|
@@ -348,13 +427,16 @@ export async function connectViteDev(logger, url, key) {
|
|
|
348
427
|
const host = target.hostname.replace(/^\[(.*)\]$/, '$1');
|
|
349
428
|
const port = Number(target.port);
|
|
350
429
|
const agent = new Agent({ keepAlive: true, maxSockets: 64 });
|
|
351
|
-
|
|
430
|
+
// Server renders on connections of their own: renders that wait (a slow or hung page) never
|
|
431
|
+
// hold the connections the browser's module requests are forwarded on.
|
|
432
|
+
const renders = new Agent({ keepAlive: true, maxSockets: 64 });
|
|
433
|
+
const call = (method, path, body, through = agent, timeoutMs) => new Promise((resolve, reject) => {
|
|
352
434
|
const outgoing = httpRequest({
|
|
353
435
|
host,
|
|
354
436
|
port,
|
|
355
437
|
method,
|
|
356
438
|
path,
|
|
357
|
-
agent,
|
|
439
|
+
agent: through,
|
|
358
440
|
headers: {
|
|
359
441
|
[VITE_HOST_KEY_HEADER]: key,
|
|
360
442
|
...(body === undefined
|
|
@@ -374,6 +456,15 @@ export async function connectViteDev(logger, url, key) {
|
|
|
374
456
|
answer.once('error', reject);
|
|
375
457
|
});
|
|
376
458
|
outgoing.once('error', reject);
|
|
459
|
+
if (timeoutMs !== undefined) {
|
|
460
|
+
const timer = setTimeout(() => {
|
|
461
|
+
const error = new InertiaError('SSR_TIMEOUT', `The Vite dev host did not finish a server render within ${timeoutMs} ms (ssr.timeoutMs)`, { data: { timeoutMs } });
|
|
462
|
+
// Settled now, also for a request still waiting for a connection (destroyRequest).
|
|
463
|
+
reject(error);
|
|
464
|
+
destroyRequest(outgoing, error);
|
|
465
|
+
}, timeoutMs);
|
|
466
|
+
outgoing.once('close', () => clearTimeout(timer));
|
|
467
|
+
}
|
|
377
468
|
outgoing.end(body);
|
|
378
469
|
});
|
|
379
470
|
try {
|
|
@@ -383,12 +474,57 @@ export async function connectViteDev(logger, url, key) {
|
|
|
383
474
|
}
|
|
384
475
|
catch (cause) {
|
|
385
476
|
agent.destroy();
|
|
477
|
+
renders.destroy();
|
|
386
478
|
if (cause instanceof InertiaError)
|
|
387
479
|
throw cause;
|
|
388
480
|
throw new InertiaError('VITE_DEV_UNAVAILABLE', `The Vite dev host at ${target.origin} cannot be reached`, { cause });
|
|
389
481
|
}
|
|
390
482
|
logger.log('debug', 'Vite dev host connected', { url: target.origin });
|
|
483
|
+
/** HMR sockets joined to the host, ended by close(). */
|
|
484
|
+
const joined = new Set();
|
|
391
485
|
return {
|
|
486
|
+
upgrade(request, socket, head, report) {
|
|
487
|
+
const upstream = connectNet({ host, port });
|
|
488
|
+
joined.add(socket);
|
|
489
|
+
joined.add(upstream);
|
|
490
|
+
let ended = false;
|
|
491
|
+
const end = () => {
|
|
492
|
+
if (ended)
|
|
493
|
+
return;
|
|
494
|
+
ended = true;
|
|
495
|
+
joined.delete(socket);
|
|
496
|
+
joined.delete(upstream);
|
|
497
|
+
socket.destroy();
|
|
498
|
+
upstream.destroy();
|
|
499
|
+
};
|
|
500
|
+
upstream.once('connect', () => {
|
|
501
|
+
// The handshake as the browser sent it (rawHeaders keeps names, order and repeats; Node
|
|
502
|
+
// refused any line break in them): Vite checks the token and the subprotocol itself.
|
|
503
|
+
const lines = [`${request.method ?? 'GET'} ${request.url ?? '/'} HTTP/1.1`];
|
|
504
|
+
const raw = request.rawHeaders;
|
|
505
|
+
for (let index = 0; index + 1 < raw.length; index += 2)
|
|
506
|
+
lines.push(`${raw[index]}: ${raw[index + 1]}`);
|
|
507
|
+
upstream.write(`${lines.join('\r\n')}\r\n\r\n`);
|
|
508
|
+
if (head.length)
|
|
509
|
+
upstream.write(head);
|
|
510
|
+
socket.pipe(upstream);
|
|
511
|
+
upstream.pipe(socket);
|
|
512
|
+
});
|
|
513
|
+
upstream.once('error', (error) => {
|
|
514
|
+
// The host is gone or refused: the browser's socket ends (its client retries).
|
|
515
|
+
if (!ended)
|
|
516
|
+
report(error);
|
|
517
|
+
end();
|
|
518
|
+
});
|
|
519
|
+
socket.once('error', (error) => {
|
|
520
|
+
// The browser or tunnel closed the connection: no fault.
|
|
521
|
+
if (logger.isEnabled('debug'))
|
|
522
|
+
logger.log('debug', 'Vite HMR socket ended by the browser', { code: error.code });
|
|
523
|
+
end();
|
|
524
|
+
});
|
|
525
|
+
socket.once('close', end);
|
|
526
|
+
upstream.once('close', end);
|
|
527
|
+
},
|
|
392
528
|
forward(request, response, report) {
|
|
393
529
|
const headers = { ...request.headers };
|
|
394
530
|
for (const name of HOP_BY_HOP)
|
|
@@ -399,7 +535,11 @@ export async function connectViteDev(logger, url, key) {
|
|
|
399
535
|
const out = { ...answer.headers };
|
|
400
536
|
for (const name of HOP_BY_HOP)
|
|
401
537
|
delete out[name];
|
|
402
|
-
|
|
538
|
+
// The reason too: Vite's 504s say which (`Outdated Optimize Dep`, D-0128).
|
|
539
|
+
if (answer.statusMessage)
|
|
540
|
+
response.writeHead(answer.statusCode ?? 502, answer.statusMessage, out);
|
|
541
|
+
else
|
|
542
|
+
response.writeHead(answer.statusCode ?? 502, out);
|
|
403
543
|
answer.pipe(response);
|
|
404
544
|
answer.once('error', (error) => {
|
|
405
545
|
if (!aborted)
|
|
@@ -428,12 +568,12 @@ export async function connectViteDev(logger, url, key) {
|
|
|
428
568
|
if (response.writableFinished)
|
|
429
569
|
return;
|
|
430
570
|
aborted = true;
|
|
431
|
-
outgoing
|
|
571
|
+
destroyRequest(outgoing);
|
|
432
572
|
});
|
|
433
573
|
outgoing.end();
|
|
434
574
|
},
|
|
435
|
-
async render(entry, page) {
|
|
436
|
-
const { status, text } = await call('POST', SSR_PATH, JSON.stringify({ entry, page }));
|
|
575
|
+
async render(entry, page, timeoutMs) {
|
|
576
|
+
const { status, text } = await call('POST', SSR_PATH, JSON.stringify({ entry, page }), renders, timeoutMs);
|
|
437
577
|
let answer;
|
|
438
578
|
try {
|
|
439
579
|
answer = JSON.parse(text);
|
|
@@ -447,6 +587,11 @@ export async function connectViteDev(logger, url, key) {
|
|
|
447
587
|
},
|
|
448
588
|
close: async () => {
|
|
449
589
|
agent.destroy();
|
|
590
|
+
renders.destroy();
|
|
591
|
+
// Open HMR sockets would keep the process (and a draining worker) alive.
|
|
592
|
+
for (const socket of [...joined])
|
|
593
|
+
socket.destroy();
|
|
594
|
+
joined.clear();
|
|
450
595
|
logger.log('debug', 'Vite dev host disconnected', { url: target.origin });
|
|
451
596
|
},
|
|
452
597
|
};
|
|
@@ -478,6 +623,7 @@ export async function openViteDev(logger, environment = process.env, root = proc
|
|
|
478
623
|
return {
|
|
479
624
|
forward: client.forward,
|
|
480
625
|
render: client.render,
|
|
626
|
+
upgrade: client.upgrade,
|
|
481
627
|
close: async () => {
|
|
482
628
|
try {
|
|
483
629
|
await client.close();
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { Logger } from '@dokebi/core';
|
|
2
|
+
/**
|
|
3
|
+
* Files the dev server never serves, whatever the app's config says (Vite globs: a pattern
|
|
4
|
+
* without `/` matches the name in every folder; letter case is ignored). Vite's own defaults are
|
|
5
|
+
* included, because a deny list replaces them.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEV_FILES_DENY: readonly string[];
|
|
8
|
+
/**
|
|
9
|
+
* Header the app process sets on every request it forwards to the Vite host: `loopback` or
|
|
10
|
+
* `remote`, from the browser's socket peer, the forwarding headers and the host name it judged
|
|
11
|
+
* (isRemoteRequest, §12). Any value the browser sent is replaced. Without it (Vite run on its
|
|
12
|
+
* own) the Vite host judges its own socket, the forwarding headers and the Host header.
|
|
13
|
+
*/
|
|
14
|
+
export declare const DEV_CLIENT_HEADER = "x-dokebi-dev-client";
|
|
15
|
+
/** The whole 403 a remote host gets: no path, no allow list. */
|
|
16
|
+
export declare const FORBIDDEN_SHORT = "403 Forbidden\n";
|
|
17
|
+
/** Whether a host name (no port, no brackets) is this machine: a remote one gets no details. */
|
|
18
|
+
export declare function isLoopbackHost(name: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Headers a proxy or tunnel adds (D-0129 §12). Any of them makes a request remote: a tunnel
|
|
21
|
+
* client runs on this machine (its socket is loopback) and may rewrite Host to localhost, so
|
|
22
|
+
* neither the socket nor the Host header alone tells a local browser from a forwarded one.
|
|
23
|
+
*/
|
|
24
|
+
export declare const FORWARDING_HEADERS: readonly string[];
|
|
25
|
+
/** Whether a socket peer address is this machine (127.0.0.0/8, ::1, ::ffff:127.x). */
|
|
26
|
+
export declare function isLoopbackAddress(address: string | undefined): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Whether a request comes from another machine (a `devHosts` tunnel, a proxy, a LAN address).
|
|
29
|
+
* Local, and so shown details, only when the socket peer is loopback, no forwarding header is
|
|
30
|
+
* present, the Host is a loopback name and the app process (DEV_CLIENT_HEADER, when set) said
|
|
31
|
+
* loopback too. An unknown peer counts as remote.
|
|
32
|
+
*/
|
|
33
|
+
export declare function isRemoteRequest(headers: Readonly<Record<string, string | string[] | undefined>>, peer: string | undefined): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* A Vite deny glob as a regular expression on an absolute `/` path, matched like Vite matches it
|
|
36
|
+
* (picomatch with `dot` and `nocase`; a pattern without `/` matches in every folder). Supports
|
|
37
|
+
* `*`, `**`, `?` and `{a,b}`, which is all DEV_FILES_DENY and ordinary app entries use.
|
|
38
|
+
*/
|
|
39
|
+
export declare function denyPattern(glob: string): RegExp;
|
|
40
|
+
/** The part of Vite's resolved config this policy reads (and repairs). */
|
|
41
|
+
export interface ResolvedFilesShape {
|
|
42
|
+
readonly command: 'build' | 'serve';
|
|
43
|
+
readonly base: string;
|
|
44
|
+
readonly root?: string;
|
|
45
|
+
readonly cacheDir?: string;
|
|
46
|
+
readonly plugins?: readonly {
|
|
47
|
+
readonly name?: string;
|
|
48
|
+
}[];
|
|
49
|
+
readonly server?: {
|
|
50
|
+
readonly fs?: {
|
|
51
|
+
strict: boolean;
|
|
52
|
+
allow: string[];
|
|
53
|
+
deny: string[];
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
/** Vite's set of files the served modules import (internal, read when present). */
|
|
57
|
+
readonly safeModulePaths?: ReadonlySet<string>;
|
|
58
|
+
}
|
|
59
|
+
/** The request and response of a connect middleware, as far as the policy needs them. */
|
|
60
|
+
export interface DevRequestShape {
|
|
61
|
+
url?: string;
|
|
62
|
+
method?: string;
|
|
63
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
64
|
+
/** The socket: its peer address is part of the loopback judgement (§12). */
|
|
65
|
+
readonly socket?: {
|
|
66
|
+
readonly remoteAddress?: string;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export interface DevResponseShape {
|
|
70
|
+
statusCode: number;
|
|
71
|
+
readonly headersSent?: boolean;
|
|
72
|
+
setHeader(name: string, value: string): unknown;
|
|
73
|
+
removeHeader?(name: string): unknown;
|
|
74
|
+
write?(chunk: unknown, ...rest: unknown[]): boolean;
|
|
75
|
+
end(body?: string): unknown;
|
|
76
|
+
}
|
|
77
|
+
export type DevMiddleware = (request: DevRequestShape, response: DevResponseShape, next: (error?: unknown) => void) => void;
|
|
78
|
+
/** What the `config` hook adds for the policy (Vite merges it into the app's config). */
|
|
79
|
+
export interface DevFilesPatch {
|
|
80
|
+
readonly server: {
|
|
81
|
+
readonly fs: {
|
|
82
|
+
readonly strict: true;
|
|
83
|
+
readonly allow: string[];
|
|
84
|
+
readonly deny: string[];
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export interface DevFilesOptions {
|
|
89
|
+
/** Further files or folders the browser loads (relative to the Vite root, or absolute). */
|
|
90
|
+
readonly allow?: readonly string[];
|
|
91
|
+
/** Further Vite globs the dev server never serves (added to DEV_FILES_DENY). */
|
|
92
|
+
readonly deny?: readonly string[];
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The dev file policy of one Vite config (one plugin instance). `owner` `dev-host` is the copy the
|
|
96
|
+
* Vite host of `dokebi dev` adds: it stays inactive when the app's config has `dokebiInertia()`.
|
|
97
|
+
*/
|
|
98
|
+
export declare class DevFilesPolicy {
|
|
99
|
+
#private;
|
|
100
|
+
constructor(logger: Logger, options: DevFilesOptions, owner: 'dokebi-inertia' | 'dev-host');
|
|
101
|
+
/** The deny entries this policy always keeps (DEV_FILES_DENY and the plugin's `deny`). */
|
|
102
|
+
get required(): string[];
|
|
103
|
+
/** The `config` hook (dev server only): the policy as a patch, and warnings for a weaker config. */
|
|
104
|
+
config(raw: unknown, command: 'build' | 'serve'): DevFilesPatch | null;
|
|
105
|
+
/** The `configResolved` hook: repairs what a later plugin loosened and keeps the live config. */
|
|
106
|
+
configResolved(config: ResolvedFilesShape): void;
|
|
107
|
+
/** The middleware to add before Vite's own (undefined when this copy is inactive). */
|
|
108
|
+
middleware(): DevMiddleware | undefined;
|
|
109
|
+
}
|
|
110
|
+
/** The Vite plugin of the policy alone (what the Vite host of `dokebi dev` adds). */
|
|
111
|
+
export interface DokebiDevFilesPlugin {
|
|
112
|
+
readonly name: 'dokebi-inertia:dev-files';
|
|
113
|
+
readonly enforce: 'pre';
|
|
114
|
+
config(config: unknown, env: {
|
|
115
|
+
command: 'build' | 'serve';
|
|
116
|
+
}): DevFilesPatch | null;
|
|
117
|
+
configResolved(config: ResolvedFilesShape): void;
|
|
118
|
+
configureServer(server: {
|
|
119
|
+
readonly middlewares: {
|
|
120
|
+
use(handler: DevMiddleware): unknown;
|
|
121
|
+
};
|
|
122
|
+
}): void;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The dev file policy as a plugin of its own, for a vite.config without `dokebiInertia()` (which
|
|
126
|
+
* carries the same policy): the Vite host of `dokebi dev` adds it; it stays inactive next to
|
|
127
|
+
* `dokebiInertia()`.
|
|
128
|
+
*/
|
|
129
|
+
export declare function dokebiDevFiles(logger: Logger, options?: DevFilesOptions): DokebiDevFilesPlugin;
|