@xbghc/warden 0.1.0 → 0.2.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.
- package/README.md +73 -15
- package/dist/cli.js +640 -123
- package/dist/web/assets/{index-BWrmu1DB.css → index-DKfCCm8U.css} +1 -1
- package/dist/web/assets/index-stdccAW_.js +192 -0
- package/dist/web/index.html +2 -2
- package/package.json +3 -3
- package/dist/web/assets/index-CZQadJRz.js +0 -192
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// bin/cli.ts
|
|
4
|
-
import
|
|
4
|
+
import path8 from "path";
|
|
5
5
|
import { spawn } from "child_process";
|
|
6
6
|
import { existsSync } from "fs";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
@@ -324,13 +324,13 @@ function writeFromReadableStreamDefaultReader(reader, writable, currentReadPromi
|
|
|
324
324
|
}
|
|
325
325
|
}
|
|
326
326
|
}
|
|
327
|
-
function writeFromReadableStream(
|
|
328
|
-
if (
|
|
327
|
+
function writeFromReadableStream(stream2, writable) {
|
|
328
|
+
if (stream2.locked) {
|
|
329
329
|
throw new TypeError("ReadableStream is locked.");
|
|
330
330
|
} else if (writable.destroyed) {
|
|
331
331
|
return;
|
|
332
332
|
}
|
|
333
|
-
return writeFromReadableStreamDefaultReader(
|
|
333
|
+
return writeFromReadableStreamDefaultReader(stream2.getReader(), writable);
|
|
334
334
|
}
|
|
335
335
|
var buildOutgoingHttpHeaders = (headers) => {
|
|
336
336
|
const res = {};
|
|
@@ -651,7 +651,7 @@ var serve = (options, listeningListener) => {
|
|
|
651
651
|
};
|
|
652
652
|
|
|
653
653
|
// packages/server/src/app.ts
|
|
654
|
-
import
|
|
654
|
+
import path7 from "path";
|
|
655
655
|
import { randomUUID } from "crypto";
|
|
656
656
|
|
|
657
657
|
// node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/compose.js
|
|
@@ -811,26 +811,26 @@ var throwNestingLimitExceeded = () => {
|
|
|
811
811
|
};
|
|
812
812
|
|
|
813
813
|
// node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/utils/url.js
|
|
814
|
-
var splitPath = (
|
|
815
|
-
const paths =
|
|
814
|
+
var splitPath = (path9) => {
|
|
815
|
+
const paths = path9.split("/");
|
|
816
816
|
if (paths[0] === "") {
|
|
817
817
|
paths.shift();
|
|
818
818
|
}
|
|
819
819
|
return paths;
|
|
820
820
|
};
|
|
821
821
|
var splitRoutingPath = (routePath) => {
|
|
822
|
-
const { groups, path:
|
|
823
|
-
const paths = splitPath(
|
|
822
|
+
const { groups, path: path9 } = extractGroupsFromPath(routePath);
|
|
823
|
+
const paths = splitPath(path9);
|
|
824
824
|
return replaceGroupMarks(paths, groups);
|
|
825
825
|
};
|
|
826
|
-
var extractGroupsFromPath = (
|
|
826
|
+
var extractGroupsFromPath = (path9) => {
|
|
827
827
|
const groups = [];
|
|
828
|
-
|
|
828
|
+
path9 = path9.replace(/\{[^}]+\}/g, (match2, index) => {
|
|
829
829
|
const mark = `@${index}`;
|
|
830
830
|
groups.push([mark, match2]);
|
|
831
831
|
return mark;
|
|
832
832
|
});
|
|
833
|
-
return { groups, path:
|
|
833
|
+
return { groups, path: path9 };
|
|
834
834
|
};
|
|
835
835
|
var replaceGroupMarks = (paths, groups) => {
|
|
836
836
|
for (let i = groups.length - 1; i >= 0; i--) {
|
|
@@ -887,8 +887,8 @@ var getPath = (request) => {
|
|
|
887
887
|
const queryIndex = url.indexOf("?", i);
|
|
888
888
|
const hashIndex = url.indexOf("#", i);
|
|
889
889
|
const end = queryIndex === -1 ? hashIndex === -1 ? void 0 : hashIndex : hashIndex === -1 ? queryIndex : Math.min(queryIndex, hashIndex);
|
|
890
|
-
const
|
|
891
|
-
return tryDecodeURI(
|
|
890
|
+
const path9 = url.slice(start, end);
|
|
891
|
+
return tryDecodeURI(path9.includes("%25") ? path9.replace(/%25/g, "%2525") : path9);
|
|
892
892
|
} else if (charCode === 63 || charCode === 35) {
|
|
893
893
|
break;
|
|
894
894
|
}
|
|
@@ -905,11 +905,11 @@ var mergePath = (base, sub, ...rest) => {
|
|
|
905
905
|
}
|
|
906
906
|
return `${base?.[0] === "/" ? "" : "/"}${base}${sub === "/" ? "" : `${base?.at(-1) === "/" ? "" : "/"}${sub?.[0] === "/" ? sub.slice(1) : sub}`}`;
|
|
907
907
|
};
|
|
908
|
-
var checkOptionalParameter = (
|
|
909
|
-
if (
|
|
908
|
+
var checkOptionalParameter = (path9) => {
|
|
909
|
+
if (path9.charCodeAt(path9.length - 1) !== 63 || !path9.includes(":")) {
|
|
910
910
|
return null;
|
|
911
911
|
}
|
|
912
|
-
const segments =
|
|
912
|
+
const segments = path9.split("/");
|
|
913
913
|
const results = [];
|
|
914
914
|
let basePath = "";
|
|
915
915
|
segments.forEach((segment) => {
|
|
@@ -1051,9 +1051,9 @@ var HonoRequest = class {
|
|
|
1051
1051
|
*/
|
|
1052
1052
|
path;
|
|
1053
1053
|
bodyCache = {};
|
|
1054
|
-
constructor(request,
|
|
1054
|
+
constructor(request, path9 = "/", matchResult = [[]]) {
|
|
1055
1055
|
this.raw = request;
|
|
1056
|
-
this.path =
|
|
1056
|
+
this.path = path9;
|
|
1057
1057
|
this.#matchResult = matchResult;
|
|
1058
1058
|
}
|
|
1059
1059
|
param(key) {
|
|
@@ -1825,8 +1825,8 @@ var Hono = class _Hono {
|
|
|
1825
1825
|
return this;
|
|
1826
1826
|
};
|
|
1827
1827
|
});
|
|
1828
|
-
this.on = (method,
|
|
1829
|
-
for (const p of [
|
|
1828
|
+
this.on = (method, path9, ...handlers) => {
|
|
1829
|
+
for (const p of [path9].flat()) {
|
|
1830
1830
|
this.#path = p;
|
|
1831
1831
|
for (const m of [method].flat()) {
|
|
1832
1832
|
const methodName = m.toUpperCase();
|
|
@@ -1884,8 +1884,8 @@ var Hono = class _Hono {
|
|
|
1884
1884
|
* app.route("/api", app2) // GET /api/user
|
|
1885
1885
|
* ```
|
|
1886
1886
|
*/
|
|
1887
|
-
route(
|
|
1888
|
-
const subApp = this.basePath(
|
|
1887
|
+
route(path9, app) {
|
|
1888
|
+
const subApp = this.basePath(path9);
|
|
1889
1889
|
app.routes.map((r) => {
|
|
1890
1890
|
let handler;
|
|
1891
1891
|
if (app.errorHandler === errorHandler) {
|
|
@@ -1911,9 +1911,9 @@ var Hono = class _Hono {
|
|
|
1911
1911
|
* const api = new Hono().basePath('/api')
|
|
1912
1912
|
* ```
|
|
1913
1913
|
*/
|
|
1914
|
-
basePath(
|
|
1914
|
+
basePath(path9) {
|
|
1915
1915
|
const subApp = this.#clone();
|
|
1916
|
-
subApp._basePath = mergePath(this._basePath,
|
|
1916
|
+
subApp._basePath = mergePath(this._basePath, path9);
|
|
1917
1917
|
return subApp;
|
|
1918
1918
|
}
|
|
1919
1919
|
/**
|
|
@@ -1987,7 +1987,7 @@ var Hono = class _Hono {
|
|
|
1987
1987
|
* })
|
|
1988
1988
|
* ```
|
|
1989
1989
|
*/
|
|
1990
|
-
mount(
|
|
1990
|
+
mount(path9, applicationHandler, options) {
|
|
1991
1991
|
let replaceRequest;
|
|
1992
1992
|
let optionHandler;
|
|
1993
1993
|
if (options) {
|
|
@@ -2014,7 +2014,7 @@ var Hono = class _Hono {
|
|
|
2014
2014
|
return [c.env, executionContext];
|
|
2015
2015
|
};
|
|
2016
2016
|
replaceRequest ||= (() => {
|
|
2017
|
-
const mergedPath = mergePath(this._basePath,
|
|
2017
|
+
const mergedPath = mergePath(this._basePath, path9);
|
|
2018
2018
|
const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
|
|
2019
2019
|
return (request) => {
|
|
2020
2020
|
const url = new URL(request.url);
|
|
@@ -2029,18 +2029,18 @@ var Hono = class _Hono {
|
|
|
2029
2029
|
}
|
|
2030
2030
|
await next();
|
|
2031
2031
|
};
|
|
2032
|
-
this.#addRoute(METHOD_NAME_ALL, mergePath(
|
|
2032
|
+
this.#addRoute(METHOD_NAME_ALL, mergePath(path9, "*"), handler);
|
|
2033
2033
|
return this;
|
|
2034
2034
|
}
|
|
2035
|
-
#addRoute(method,
|
|
2036
|
-
|
|
2035
|
+
#addRoute(method, path9, handler, baseRoutePath) {
|
|
2036
|
+
path9 = mergePath(this._basePath, path9);
|
|
2037
2037
|
const r = {
|
|
2038
2038
|
basePath: baseRoutePath !== void 0 ? mergePath(this._basePath, baseRoutePath) : this._basePath,
|
|
2039
|
-
path:
|
|
2039
|
+
path: path9,
|
|
2040
2040
|
method,
|
|
2041
2041
|
handler
|
|
2042
2042
|
};
|
|
2043
|
-
this.router.add(method,
|
|
2043
|
+
this.router.add(method, path9, [handler, r]);
|
|
2044
2044
|
this.routes.push(r);
|
|
2045
2045
|
}
|
|
2046
2046
|
#handleError(err, c) {
|
|
@@ -2053,10 +2053,10 @@ var Hono = class _Hono {
|
|
|
2053
2053
|
if (method === "HEAD") {
|
|
2054
2054
|
return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, "GET")))();
|
|
2055
2055
|
}
|
|
2056
|
-
const
|
|
2057
|
-
const matchResult = this.router.match(method,
|
|
2056
|
+
const path9 = this.getPath(request, { env });
|
|
2057
|
+
const matchResult = this.router.match(method, path9);
|
|
2058
2058
|
const c = new Context(request, {
|
|
2059
|
-
path:
|
|
2059
|
+
path: path9,
|
|
2060
2060
|
matchResult,
|
|
2061
2061
|
env,
|
|
2062
2062
|
executionCtx,
|
|
@@ -2159,7 +2159,7 @@ var createNullObject = () => /* @__PURE__ */ Object.create(null);
|
|
|
2159
2159
|
|
|
2160
2160
|
// node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/reg-exp-router/matcher.js
|
|
2161
2161
|
var emptyParam = [];
|
|
2162
|
-
function match(method,
|
|
2162
|
+
function match(method, path9) {
|
|
2163
2163
|
const matchers = this.buildAllMatchers();
|
|
2164
2164
|
const match2 = ((method2, path22) => {
|
|
2165
2165
|
const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
|
|
@@ -2175,7 +2175,7 @@ function match(method, path8) {
|
|
|
2175
2175
|
return [matcher[1][index], match3];
|
|
2176
2176
|
});
|
|
2177
2177
|
this.match = match2;
|
|
2178
|
-
return match2(method,
|
|
2178
|
+
return match2(method, path9);
|
|
2179
2179
|
}
|
|
2180
2180
|
|
|
2181
2181
|
// node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/reg-exp-router/node.js
|
|
@@ -2292,14 +2292,14 @@ var Trie = class {
|
|
|
2292
2292
|
#index = 0;
|
|
2293
2293
|
// dynamic path -> [handler index, param assoc]; static paths are not registered
|
|
2294
2294
|
paths = createNullObject();
|
|
2295
|
-
insert(
|
|
2295
|
+
insert(path9, isStatic) {
|
|
2296
2296
|
if (isStatic) {
|
|
2297
|
-
this.#root.insert(
|
|
2297
|
+
this.#root.insert(path9.split(""), 0, [], this.#context, true);
|
|
2298
2298
|
return;
|
|
2299
2299
|
}
|
|
2300
2300
|
const paramAssoc = [];
|
|
2301
2301
|
const groups = [];
|
|
2302
|
-
let markedPath =
|
|
2302
|
+
let markedPath = path9;
|
|
2303
2303
|
for (let i = 0; ; ) {
|
|
2304
2304
|
let replaced = false;
|
|
2305
2305
|
markedPath = markedPath.replace(/\{[^}]+\}/g, (m) => {
|
|
@@ -2324,7 +2324,7 @@ var Trie = class {
|
|
|
2324
2324
|
}
|
|
2325
2325
|
}
|
|
2326
2326
|
this.#root.insert(tokens, this.#index, paramAssoc, this.#context, false);
|
|
2327
|
-
this.paths[
|
|
2327
|
+
this.paths[path9] = [this.#index++, paramAssoc];
|
|
2328
2328
|
}
|
|
2329
2329
|
buildRegExp() {
|
|
2330
2330
|
let regexp = this.#root.buildRegExpStr();
|
|
@@ -2351,17 +2351,17 @@ var Trie = class {
|
|
|
2351
2351
|
|
|
2352
2352
|
// node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/reg-exp-router/router.js
|
|
2353
2353
|
var wildcardRegExpCache = createNullObject();
|
|
2354
|
-
function buildWildcardRegExp(
|
|
2355
|
-
return wildcardRegExpCache[
|
|
2356
|
-
`^${
|
|
2354
|
+
function buildWildcardRegExp(path9) {
|
|
2355
|
+
return wildcardRegExpCache[path9] ??= new RegExp(
|
|
2356
|
+
`^${path9.replace(
|
|
2357
2357
|
/\/:[^/{}]+(?:\{\[\^\/]\+})?(?=[/{]|$)|\/?\*$|([.\\+*[^\]$()?{}|])/g,
|
|
2358
2358
|
(match2, metaChar) => metaChar ? `\\${metaChar}` : match2 === "/*" ? TAIL_WILDCARD_REG_EXP_STR : match2 === "*" ? ONLY_WILDCARD_REG_EXP_STR : `/:${LABEL_REG_EXP_STR}`
|
|
2359
2359
|
)}$`
|
|
2360
2360
|
);
|
|
2361
2361
|
}
|
|
2362
|
-
function findMiddleware(middleware,
|
|
2362
|
+
function findMiddleware(middleware, path9) {
|
|
2363
2363
|
for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) {
|
|
2364
|
-
if (buildWildcardRegExp(k).test(
|
|
2364
|
+
if (buildWildcardRegExp(k).test(path9)) {
|
|
2365
2365
|
return [...middleware[k]];
|
|
2366
2366
|
}
|
|
2367
2367
|
}
|
|
@@ -2377,14 +2377,14 @@ var RegExpRouter = class {
|
|
|
2377
2377
|
this.#routes = { [METHOD_NAME_ALL]: createNullObject() };
|
|
2378
2378
|
this.#tries = { [METHOD_NAME_ALL]: new Trie() };
|
|
2379
2379
|
}
|
|
2380
|
-
#insertPath(method,
|
|
2380
|
+
#insertPath(method, path9) {
|
|
2381
2381
|
try {
|
|
2382
|
-
this.#tries[method].insert(
|
|
2382
|
+
this.#tries[method].insert(path9, !/\*|\/:/.test(path9));
|
|
2383
2383
|
} catch (e) {
|
|
2384
|
-
throw e === PATH_ERROR ? new UnsupportedPathError(
|
|
2384
|
+
throw e === PATH_ERROR ? new UnsupportedPathError(path9) : e;
|
|
2385
2385
|
}
|
|
2386
2386
|
}
|
|
2387
|
-
add(method,
|
|
2387
|
+
add(method, path9, handler) {
|
|
2388
2388
|
const middleware = this.#middleware;
|
|
2389
2389
|
const routes = this.#routes;
|
|
2390
2390
|
if (!middleware) {
|
|
@@ -2400,28 +2400,28 @@ var RegExpRouter = class {
|
|
|
2400
2400
|
}
|
|
2401
2401
|
}
|
|
2402
2402
|
}
|
|
2403
|
-
if (
|
|
2404
|
-
|
|
2403
|
+
if (path9 === "/*") {
|
|
2404
|
+
path9 = "*";
|
|
2405
2405
|
}
|
|
2406
2406
|
const methods = method === METHOD_NAME_ALL ? Object.keys(middleware) : [method];
|
|
2407
|
-
if (/\*$/.test(
|
|
2408
|
-
const re = buildWildcardRegExp(
|
|
2407
|
+
if (/\*$/.test(path9)) {
|
|
2408
|
+
const re = buildWildcardRegExp(path9);
|
|
2409
2409
|
for (const m of methods) {
|
|
2410
|
-
if (!middleware[m][
|
|
2411
|
-
this.#insertPath(m,
|
|
2412
|
-
middleware[m][
|
|
2410
|
+
if (!middleware[m][path9]) {
|
|
2411
|
+
this.#insertPath(m, path9);
|
|
2412
|
+
middleware[m][path9] = findMiddleware(middleware[m], path9) || findMiddleware(middleware[METHOD_NAME_ALL], path9) || [];
|
|
2413
2413
|
}
|
|
2414
2414
|
}
|
|
2415
2415
|
for (const handlerMap of [middleware, routes]) {
|
|
2416
2416
|
for (const m of methods) {
|
|
2417
2417
|
for (const p in handlerMap[m]) {
|
|
2418
|
-
re.test(p) && handlerMap[m][p].push([handler,
|
|
2418
|
+
re.test(p) && handlerMap[m][p].push([handler, path9]);
|
|
2419
2419
|
}
|
|
2420
2420
|
}
|
|
2421
2421
|
}
|
|
2422
2422
|
return;
|
|
2423
2423
|
}
|
|
2424
|
-
const paths = checkOptionalParameter(
|
|
2424
|
+
const paths = checkOptionalParameter(path9) || [path9];
|
|
2425
2425
|
for (const path22 of paths) {
|
|
2426
2426
|
for (const m of methods) {
|
|
2427
2427
|
if (!routes[m][path22]) {
|
|
@@ -2450,11 +2450,11 @@ var RegExpRouter = class {
|
|
|
2450
2450
|
const handlerData = [];
|
|
2451
2451
|
const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
|
|
2452
2452
|
for (const r of [middleware, routes]) {
|
|
2453
|
-
for (const
|
|
2454
|
-
const handlers = r[
|
|
2455
|
-
const pathData = trie.paths[
|
|
2453
|
+
for (const path9 in r) {
|
|
2454
|
+
const handlers = r[path9];
|
|
2455
|
+
const pathData = trie.paths[path9];
|
|
2456
2456
|
if (!pathData) {
|
|
2457
|
-
staticMap[
|
|
2457
|
+
staticMap[path9] = [handlers.map(([h]) => [h, createNullObject()]), emptyParam];
|
|
2458
2458
|
continue;
|
|
2459
2459
|
}
|
|
2460
2460
|
handlerData[pathData[0]] = handlers.map(([h, handlerPath]) => [
|
|
@@ -2478,13 +2478,13 @@ var SmartRouter = class {
|
|
|
2478
2478
|
constructor(init) {
|
|
2479
2479
|
this.#routers = init.routers;
|
|
2480
2480
|
}
|
|
2481
|
-
add(method,
|
|
2481
|
+
add(method, path9, handler) {
|
|
2482
2482
|
if (!this.#routes) {
|
|
2483
2483
|
throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
|
|
2484
2484
|
}
|
|
2485
|
-
this.#routes.push([method,
|
|
2485
|
+
this.#routes.push([method, path9, handler]);
|
|
2486
2486
|
}
|
|
2487
|
-
match(method,
|
|
2487
|
+
match(method, path9) {
|
|
2488
2488
|
if (!this.#routes) {
|
|
2489
2489
|
throw new Error("Fatal error");
|
|
2490
2490
|
}
|
|
@@ -2499,7 +2499,7 @@ var SmartRouter = class {
|
|
|
2499
2499
|
for (let i2 = 0, len2 = routes.length; i2 < len2; i2++) {
|
|
2500
2500
|
router.add(...routes[i2]);
|
|
2501
2501
|
}
|
|
2502
|
-
res = router.match(method,
|
|
2502
|
+
res = router.match(method, path9);
|
|
2503
2503
|
} catch (e) {
|
|
2504
2504
|
if (e instanceof UnsupportedPathError) {
|
|
2505
2505
|
continue;
|
|
@@ -2534,9 +2534,9 @@ var Node2 = class _Node2 {
|
|
|
2534
2534
|
#patterns = [];
|
|
2535
2535
|
#pattern;
|
|
2536
2536
|
#params = emptyParams;
|
|
2537
|
-
insert(method,
|
|
2537
|
+
insert(method, path9, handler) {
|
|
2538
2538
|
let curNode = this;
|
|
2539
|
-
const parts = splitRoutingPath(
|
|
2539
|
+
const parts = splitRoutingPath(path9);
|
|
2540
2540
|
const possibleKeys = /* @__PURE__ */ new Set();
|
|
2541
2541
|
let i = 0;
|
|
2542
2542
|
for (const p of parts) {
|
|
@@ -2576,12 +2576,12 @@ var Node2 = class _Node2 {
|
|
|
2576
2576
|
}
|
|
2577
2577
|
}
|
|
2578
2578
|
}
|
|
2579
|
-
search(method,
|
|
2579
|
+
search(method, path9) {
|
|
2580
2580
|
const handlerSets = [];
|
|
2581
2581
|
this.#params = emptyParams;
|
|
2582
2582
|
const curNode = this;
|
|
2583
2583
|
let curNodes = [curNode];
|
|
2584
|
-
const parts = splitPath(
|
|
2584
|
+
const parts = splitPath(path9);
|
|
2585
2585
|
const curNodesQueue = [];
|
|
2586
2586
|
const len = parts.length;
|
|
2587
2587
|
let partOffsets = null;
|
|
@@ -2623,13 +2623,13 @@ var Node2 = class _Node2 {
|
|
|
2623
2623
|
if (matcher !== true) {
|
|
2624
2624
|
if (!partOffsets) {
|
|
2625
2625
|
partOffsets = [];
|
|
2626
|
-
let offset =
|
|
2626
|
+
let offset = path9[0] === "/" ? 1 : 0;
|
|
2627
2627
|
for (let p = 0; p < len; p++) {
|
|
2628
2628
|
partOffsets[p] = offset;
|
|
2629
2629
|
offset += parts[p].length + 1;
|
|
2630
2630
|
}
|
|
2631
2631
|
}
|
|
2632
|
-
const restPathString =
|
|
2632
|
+
const restPathString = path9.slice(partOffsets[i]);
|
|
2633
2633
|
const m = matcher.exec(restPathString);
|
|
2634
2634
|
if (m) {
|
|
2635
2635
|
params[name] = m[0];
|
|
@@ -2689,13 +2689,13 @@ var Node2 = class _Node2 {
|
|
|
2689
2689
|
var TrieRouter = class {
|
|
2690
2690
|
name = "TrieRouter";
|
|
2691
2691
|
#node = new Node2();
|
|
2692
|
-
add(method,
|
|
2693
|
-
for (const result of checkOptionalParameter(
|
|
2692
|
+
add(method, path9, handler) {
|
|
2693
|
+
for (const result of checkOptionalParameter(path9) || [path9]) {
|
|
2694
2694
|
this.#node.insert(method, result, handler);
|
|
2695
2695
|
}
|
|
2696
2696
|
}
|
|
2697
|
-
match(method,
|
|
2698
|
-
return this.#node.search(method,
|
|
2697
|
+
match(method, path9) {
|
|
2698
|
+
return this.#node.search(method, path9);
|
|
2699
2699
|
}
|
|
2700
2700
|
};
|
|
2701
2701
|
|
|
@@ -2714,6 +2714,167 @@ var Hono2 = class extends Hono {
|
|
|
2714
2714
|
}
|
|
2715
2715
|
};
|
|
2716
2716
|
|
|
2717
|
+
// node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/utils/stream.js
|
|
2718
|
+
var StreamingApi = class {
|
|
2719
|
+
writer;
|
|
2720
|
+
encoder;
|
|
2721
|
+
writable;
|
|
2722
|
+
abortSubscribers = [];
|
|
2723
|
+
responseReadable;
|
|
2724
|
+
/**
|
|
2725
|
+
* Whether the stream has been aborted.
|
|
2726
|
+
*/
|
|
2727
|
+
aborted = false;
|
|
2728
|
+
/**
|
|
2729
|
+
* Whether the stream has been closed normally.
|
|
2730
|
+
*/
|
|
2731
|
+
closed = false;
|
|
2732
|
+
constructor(writable, _readable) {
|
|
2733
|
+
this.writable = writable;
|
|
2734
|
+
this.writer = writable.getWriter();
|
|
2735
|
+
this.encoder = new TextEncoder();
|
|
2736
|
+
const reader = _readable.getReader();
|
|
2737
|
+
this.abortSubscribers.push(async () => {
|
|
2738
|
+
await reader.cancel();
|
|
2739
|
+
});
|
|
2740
|
+
this.responseReadable = new ReadableStream({
|
|
2741
|
+
async pull(controller) {
|
|
2742
|
+
const { done, value } = await reader.read();
|
|
2743
|
+
done ? controller.close() : controller.enqueue(value);
|
|
2744
|
+
},
|
|
2745
|
+
cancel: () => {
|
|
2746
|
+
if (!this.closed) {
|
|
2747
|
+
this.abort();
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
});
|
|
2751
|
+
}
|
|
2752
|
+
async write(input) {
|
|
2753
|
+
try {
|
|
2754
|
+
if (typeof input === "string") {
|
|
2755
|
+
input = this.encoder.encode(input);
|
|
2756
|
+
}
|
|
2757
|
+
await this.writer.write(input);
|
|
2758
|
+
} catch {
|
|
2759
|
+
}
|
|
2760
|
+
return this;
|
|
2761
|
+
}
|
|
2762
|
+
async writeln(input) {
|
|
2763
|
+
await this.write(input + "\n");
|
|
2764
|
+
return this;
|
|
2765
|
+
}
|
|
2766
|
+
sleep(ms) {
|
|
2767
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
2768
|
+
}
|
|
2769
|
+
async close() {
|
|
2770
|
+
this.closed = true;
|
|
2771
|
+
try {
|
|
2772
|
+
await this.writer.close();
|
|
2773
|
+
} catch {
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
async pipe(body) {
|
|
2777
|
+
this.writer.releaseLock();
|
|
2778
|
+
try {
|
|
2779
|
+
await body.pipeTo(this.writable, { preventClose: true, preventAbort: true });
|
|
2780
|
+
} finally {
|
|
2781
|
+
this.writer = this.writable.getWriter();
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
onAbort(listener) {
|
|
2785
|
+
this.abortSubscribers.push(listener);
|
|
2786
|
+
}
|
|
2787
|
+
/**
|
|
2788
|
+
* Abort the stream.
|
|
2789
|
+
* You can call this method when stream is aborted by external event.
|
|
2790
|
+
*/
|
|
2791
|
+
abort() {
|
|
2792
|
+
if (!this.aborted) {
|
|
2793
|
+
this.aborted = true;
|
|
2794
|
+
this.abortSubscribers.forEach((subscriber) => {
|
|
2795
|
+
try {
|
|
2796
|
+
void Promise.resolve(subscriber()).catch(() => {
|
|
2797
|
+
});
|
|
2798
|
+
} catch {
|
|
2799
|
+
}
|
|
2800
|
+
});
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
};
|
|
2804
|
+
|
|
2805
|
+
// node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/helper/streaming/utils.js
|
|
2806
|
+
var isOldBunVersion = () => {
|
|
2807
|
+
const version = typeof Bun !== "undefined" ? Bun.version : void 0;
|
|
2808
|
+
if (version === void 0) {
|
|
2809
|
+
return false;
|
|
2810
|
+
}
|
|
2811
|
+
const result = version.startsWith("1.1") || version.startsWith("1.0") || version.startsWith("0.");
|
|
2812
|
+
isOldBunVersion = () => result;
|
|
2813
|
+
return result;
|
|
2814
|
+
};
|
|
2815
|
+
|
|
2816
|
+
// node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/helper/streaming/sse.js
|
|
2817
|
+
var SSEStreamingApi = class extends StreamingApi {
|
|
2818
|
+
constructor(writable, readable) {
|
|
2819
|
+
super(writable, readable);
|
|
2820
|
+
}
|
|
2821
|
+
async writeSSE(message) {
|
|
2822
|
+
const data = await resolveCallback(message.data, HtmlEscapedCallbackPhase.Stringify, false, {});
|
|
2823
|
+
const dataLines = data.split(/\r\n|\r|\n/).map((line) => {
|
|
2824
|
+
return `data: ${line}`;
|
|
2825
|
+
}).join("\n");
|
|
2826
|
+
for (const key of ["event", "id"]) {
|
|
2827
|
+
const value = message[key];
|
|
2828
|
+
if (value && /[\r\n]/.test(value)) {
|
|
2829
|
+
throw new Error(`${key} must not contain "\\r" or "\\n"`);
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
const sseData = [
|
|
2833
|
+
message.event && `event: ${message.event}`,
|
|
2834
|
+
dataLines,
|
|
2835
|
+
message.id !== void 0 && `id: ${message.id}`,
|
|
2836
|
+
message.retry !== void 0 && `retry: ${message.retry}`
|
|
2837
|
+
].filter(Boolean).join("\n") + "\n\n";
|
|
2838
|
+
await this.write(sseData);
|
|
2839
|
+
}
|
|
2840
|
+
};
|
|
2841
|
+
var run = async (stream2, cb, onError) => {
|
|
2842
|
+
try {
|
|
2843
|
+
await cb(stream2);
|
|
2844
|
+
} catch (e) {
|
|
2845
|
+
if (e instanceof Error && onError) {
|
|
2846
|
+
await onError(e, stream2);
|
|
2847
|
+
await stream2.writeSSE({
|
|
2848
|
+
event: "error",
|
|
2849
|
+
data: e.message
|
|
2850
|
+
});
|
|
2851
|
+
} else {
|
|
2852
|
+
console.error(e);
|
|
2853
|
+
}
|
|
2854
|
+
} finally {
|
|
2855
|
+
stream2.close();
|
|
2856
|
+
}
|
|
2857
|
+
};
|
|
2858
|
+
var contextStash = /* @__PURE__ */ new WeakMap();
|
|
2859
|
+
var streamSSE = (c, cb, onError) => {
|
|
2860
|
+
const { readable, writable } = new TransformStream();
|
|
2861
|
+
const stream2 = new SSEStreamingApi(writable, readable);
|
|
2862
|
+
if (isOldBunVersion()) {
|
|
2863
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
2864
|
+
if (!stream2.closed) {
|
|
2865
|
+
stream2.abort();
|
|
2866
|
+
}
|
|
2867
|
+
});
|
|
2868
|
+
}
|
|
2869
|
+
contextStash.set(stream2.responseReadable, c);
|
|
2870
|
+
c.header("Transfer-Encoding", "chunked");
|
|
2871
|
+
c.header("Content-Type", "text/event-stream");
|
|
2872
|
+
c.header("Cache-Control", "no-cache");
|
|
2873
|
+
c.header("Connection", "keep-alive");
|
|
2874
|
+
run(stream2, cb, onError);
|
|
2875
|
+
return c.newResponse(stream2.responseReadable);
|
|
2876
|
+
};
|
|
2877
|
+
|
|
2717
2878
|
// packages/shared/src/target.ts
|
|
2718
2879
|
var TargetKeyError = class extends Error {
|
|
2719
2880
|
constructor(message) {
|
|
@@ -2758,6 +2919,47 @@ function parseTargetKey(key) {
|
|
|
2758
2919
|
}
|
|
2759
2920
|
return parseSuffix(key);
|
|
2760
2921
|
}
|
|
2922
|
+
function formatTargetKey(t) {
|
|
2923
|
+
let suffix;
|
|
2924
|
+
switch (t.kind) {
|
|
2925
|
+
case "working":
|
|
2926
|
+
case "staged":
|
|
2927
|
+
case "all":
|
|
2928
|
+
suffix = t.kind;
|
|
2929
|
+
break;
|
|
2930
|
+
case "commit":
|
|
2931
|
+
suffix = `commit:${t.sha}`;
|
|
2932
|
+
break;
|
|
2933
|
+
case "range":
|
|
2934
|
+
suffix = `range:${t.base}..${t.head}`;
|
|
2935
|
+
break;
|
|
2936
|
+
}
|
|
2937
|
+
return t.worktree ? `worktree:${t.worktree}:${suffix}` : suffix;
|
|
2938
|
+
}
|
|
2939
|
+
function tryParseTargetKey(key) {
|
|
2940
|
+
try {
|
|
2941
|
+
return parseTargetKey(key);
|
|
2942
|
+
} catch {
|
|
2943
|
+
return void 0;
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
function isLocalTarget(t) {
|
|
2947
|
+
return t.kind === "working" || t.kind === "staged" || t.kind === "all";
|
|
2948
|
+
}
|
|
2949
|
+
function commentScopeKey(key) {
|
|
2950
|
+
const t = tryParseTargetKey(key);
|
|
2951
|
+
if (!t || !isLocalTarget(t)) return key;
|
|
2952
|
+
return t.worktree ? `worktree:${t.worktree}:local` : "local";
|
|
2953
|
+
}
|
|
2954
|
+
function localViewKeys(key) {
|
|
2955
|
+
const t = tryParseTargetKey(key);
|
|
2956
|
+
const wt = t?.worktree ? { worktree: t.worktree } : {};
|
|
2957
|
+
return [
|
|
2958
|
+
formatTargetKey({ kind: "working", ...wt }),
|
|
2959
|
+
formatTargetKey({ kind: "staged", ...wt }),
|
|
2960
|
+
formatTargetKey({ kind: "all", ...wt })
|
|
2961
|
+
];
|
|
2962
|
+
}
|
|
2761
2963
|
|
|
2762
2964
|
// packages/server/src/errors.ts
|
|
2763
2965
|
var HttpError = class extends Error {
|
|
@@ -2939,16 +3141,24 @@ async function listWorktrees(ctx) {
|
|
|
2939
3141
|
}
|
|
2940
3142
|
return out;
|
|
2941
3143
|
}
|
|
3144
|
+
async function currentBranch(cwd) {
|
|
3145
|
+
const res = await runGit(["rev-parse", "--abbrev-ref", "HEAD"], { cwd }).catch(() => ({ stdout: "" }));
|
|
3146
|
+
const name = res.stdout.trim();
|
|
3147
|
+
if (name && name !== "HEAD") return name;
|
|
3148
|
+
const head = await runGit(["rev-parse", "--verify", "--quiet", "HEAD"], { cwd }).catch(() => ({ stdout: "" }));
|
|
3149
|
+
const sha = head.stdout.trim();
|
|
3150
|
+
return sha ? sha.slice(0, 7) : "HEAD";
|
|
3151
|
+
}
|
|
2942
3152
|
async function getRepoInfo(ctx, defaultTarget) {
|
|
2943
|
-
const [
|
|
2944
|
-
|
|
3153
|
+
const [branch, headRes, worktrees] = await Promise.all([
|
|
3154
|
+
currentBranch(ctx.root),
|
|
2945
3155
|
runGit(["rev-parse", "HEAD"], { cwd: ctx.root }).catch(() => ({ stdout: "" })),
|
|
2946
3156
|
listWorktrees(ctx)
|
|
2947
3157
|
]);
|
|
2948
3158
|
return {
|
|
2949
3159
|
root: ctx.root,
|
|
2950
3160
|
commonRoot: ctx.commonRoot,
|
|
2951
|
-
branch
|
|
3161
|
+
branch,
|
|
2952
3162
|
head: headRes.stdout.trim(),
|
|
2953
3163
|
worktrees,
|
|
2954
3164
|
defaultTarget
|
|
@@ -3459,10 +3669,10 @@ function stateFilePath(repoRoot, baseDir = dataDir()) {
|
|
|
3459
3669
|
return path3.join(baseDir, repoHash(repoRoot), "state.json");
|
|
3460
3670
|
}
|
|
3461
3671
|
function defaultPrefs() {
|
|
3462
|
-
return { viewMode: "unified", nvimSocketByRoot: {} };
|
|
3672
|
+
return { viewMode: "unified", nvimSocketByRoot: {}, autoRefresh: true };
|
|
3463
3673
|
}
|
|
3464
3674
|
function defaultState(repoRoot) {
|
|
3465
|
-
return { schemaVersion: 1, repoRoot, targets: {}, issues: [], prefs: defaultPrefs() };
|
|
3675
|
+
return { schemaVersion: 1, repoRoot, targets: {}, issues: [], todos: [], prefs: defaultPrefs() };
|
|
3466
3676
|
}
|
|
3467
3677
|
function ensureTarget(state, key) {
|
|
3468
3678
|
let t = state.targets[key];
|
|
@@ -3482,17 +3692,40 @@ function normalise(raw2, repoRoot) {
|
|
|
3482
3692
|
if (!v || typeof v !== "object") continue;
|
|
3483
3693
|
targets[k] = {
|
|
3484
3694
|
viewed: typeof v.viewed === "object" && v.viewed ? v.viewed : {},
|
|
3485
|
-
comments: Array.isArray(v.comments) ? v.comments : []
|
|
3695
|
+
comments: Array.isArray(v.comments) ? v.comments : [],
|
|
3696
|
+
...typeof v.head === "string" ? { head: v.head } : {}
|
|
3486
3697
|
};
|
|
3487
3698
|
}
|
|
3699
|
+
migrateLocalComments(targets);
|
|
3488
3700
|
return {
|
|
3489
3701
|
schemaVersion: 1,
|
|
3490
3702
|
repoRoot: r.repoRoot ?? repoRoot,
|
|
3491
3703
|
targets,
|
|
3492
3704
|
issues: Array.isArray(r.issues) ? r.issues : [],
|
|
3493
|
-
|
|
3705
|
+
todos: Array.isArray(r.todos) ? r.todos : [],
|
|
3706
|
+
prefs: {
|
|
3707
|
+
...defaultPrefs(),
|
|
3708
|
+
...r.prefs ?? {},
|
|
3709
|
+
nvimSocketByRoot: r.prefs?.nvimSocketByRoot ?? {},
|
|
3710
|
+
autoRefresh: typeof r.prefs?.autoRefresh === "boolean" ? r.prefs.autoRefresh : true
|
|
3711
|
+
}
|
|
3494
3712
|
};
|
|
3495
3713
|
}
|
|
3714
|
+
function migrateLocalComments(targets) {
|
|
3715
|
+
for (const key of Object.keys(targets).sort()) {
|
|
3716
|
+
const scope = commentScopeKey(key);
|
|
3717
|
+
if (scope === key) continue;
|
|
3718
|
+
const src = targets[key];
|
|
3719
|
+
if (src.comments.length === 0) continue;
|
|
3720
|
+
let dst = targets[scope];
|
|
3721
|
+
if (!dst) {
|
|
3722
|
+
dst = { viewed: {}, comments: [] };
|
|
3723
|
+
targets[scope] = dst;
|
|
3724
|
+
}
|
|
3725
|
+
dst.comments.push(...src.comments);
|
|
3726
|
+
src.comments = [];
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3496
3729
|
var LOCK_STALE_MS = 5e3;
|
|
3497
3730
|
var LOCK_WAIT_MS = 5e3;
|
|
3498
3731
|
async function sleep(ms) {
|
|
@@ -3525,7 +3758,7 @@ var StateStore = class {
|
|
|
3525
3758
|
}
|
|
3526
3759
|
/** Apply a mutation under lock. The callback receives the freshest on-disk state. */
|
|
3527
3760
|
update(fn) {
|
|
3528
|
-
const
|
|
3761
|
+
const run2 = async () => {
|
|
3529
3762
|
await mkdir(path3.dirname(this.file), { recursive: true });
|
|
3530
3763
|
const release = await this.acquireLock();
|
|
3531
3764
|
try {
|
|
@@ -3537,7 +3770,7 @@ var StateStore = class {
|
|
|
3537
3770
|
await release();
|
|
3538
3771
|
}
|
|
3539
3772
|
};
|
|
3540
|
-
const p = this.queue.then(
|
|
3773
|
+
const p = this.queue.then(run2, run2);
|
|
3541
3774
|
this.queue = p.catch(() => void 0);
|
|
3542
3775
|
return p;
|
|
3543
3776
|
}
|
|
@@ -3658,6 +3891,16 @@ function formatIssueExport({ repoRoot, issue, comments }) {
|
|
|
3658
3891
|
parts.push(...comments.map(formatCommentSection));
|
|
3659
3892
|
return parts.join("\n\n") + "\n";
|
|
3660
3893
|
}
|
|
3894
|
+
function formatTodosExport({ repoRoot, branch, todos }) {
|
|
3895
|
+
const head = ["# TODO", `Branch: ${branch}`, `Repo: ${repoRoot}`, `Count: ${todos.length}`];
|
|
3896
|
+
const sections = todos.map((t, i) => {
|
|
3897
|
+
const title = `## ${i + 1}. ${t.title}${t.status === "done" ? " (done)" : ""}`;
|
|
3898
|
+
const body = t.body.replace(/\r\n/g, "\n").trim();
|
|
3899
|
+
return body ? `${title}
|
|
3900
|
+
${body}` : title;
|
|
3901
|
+
});
|
|
3902
|
+
return [head.join("\n"), ...sections].join("\n\n") + "\n";
|
|
3903
|
+
}
|
|
3661
3904
|
|
|
3662
3905
|
// packages/server/src/nvim.ts
|
|
3663
3906
|
import os2 from "os";
|
|
@@ -3778,9 +4021,116 @@ var NvimService = class {
|
|
|
3778
4021
|
}
|
|
3779
4022
|
};
|
|
3780
4023
|
|
|
3781
|
-
// packages/server/src/
|
|
4024
|
+
// packages/server/src/watcher.ts
|
|
3782
4025
|
import path5 from "path";
|
|
3783
|
-
import {
|
|
4026
|
+
import { stat as stat3 } from "fs/promises";
|
|
4027
|
+
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
4028
|
+
function statusPaths(out) {
|
|
4029
|
+
const parts = out.split("\0");
|
|
4030
|
+
const paths = [];
|
|
4031
|
+
for (let i = 0; i < parts.length; i++) {
|
|
4032
|
+
const entry = parts[i];
|
|
4033
|
+
if (!entry || entry.length < 3) continue;
|
|
4034
|
+
const x = entry[0];
|
|
4035
|
+
const y = entry[1];
|
|
4036
|
+
const p = entry.slice(3);
|
|
4037
|
+
if (p) paths.push(p);
|
|
4038
|
+
if (x === "R" || x === "C" || y === "R" || y === "C") {
|
|
4039
|
+
const origin = parts[++i];
|
|
4040
|
+
if (origin) paths.push(origin);
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
return paths;
|
|
4044
|
+
}
|
|
4045
|
+
var RepoWatcher = class {
|
|
4046
|
+
constructor(root, intervalMs = DEFAULT_POLL_INTERVAL_MS) {
|
|
4047
|
+
this.root = root;
|
|
4048
|
+
this.intervalMs = intervalMs;
|
|
4049
|
+
}
|
|
4050
|
+
root;
|
|
4051
|
+
intervalMs;
|
|
4052
|
+
listeners = /* @__PURE__ */ new Set();
|
|
4053
|
+
timer = null;
|
|
4054
|
+
signature = null;
|
|
4055
|
+
polling = false;
|
|
4056
|
+
get subscriberCount() {
|
|
4057
|
+
return this.listeners.size;
|
|
4058
|
+
}
|
|
4059
|
+
subscribe(listener) {
|
|
4060
|
+
this.listeners.add(listener);
|
|
4061
|
+
this.start();
|
|
4062
|
+
let released = false;
|
|
4063
|
+
return () => {
|
|
4064
|
+
if (released) return;
|
|
4065
|
+
released = true;
|
|
4066
|
+
this.listeners.delete(listener);
|
|
4067
|
+
if (this.listeners.size === 0) this.stop();
|
|
4068
|
+
};
|
|
4069
|
+
}
|
|
4070
|
+
start() {
|
|
4071
|
+
if (this.timer) return;
|
|
4072
|
+
this.timer = setInterval(() => void this.poll(), this.intervalMs);
|
|
4073
|
+
this.timer.unref?.();
|
|
4074
|
+
void this.poll();
|
|
4075
|
+
}
|
|
4076
|
+
stop() {
|
|
4077
|
+
if (this.timer) clearInterval(this.timer);
|
|
4078
|
+
this.timer = null;
|
|
4079
|
+
this.signature = null;
|
|
4080
|
+
}
|
|
4081
|
+
/**
|
|
4082
|
+
* Runs one comparison. Returns the change that was broadcast, or undefined when nothing moved.
|
|
4083
|
+
* The first poll of a watch only records the baseline. Public so tests can step it deterministically.
|
|
4084
|
+
*/
|
|
4085
|
+
async poll() {
|
|
4086
|
+
if (this.polling) return void 0;
|
|
4087
|
+
this.polling = true;
|
|
4088
|
+
try {
|
|
4089
|
+
const snap = await this.snapshot();
|
|
4090
|
+
const baseline = this.signature === null;
|
|
4091
|
+
if (this.signature === snap.signature) return void 0;
|
|
4092
|
+
this.signature = snap.signature;
|
|
4093
|
+
if (baseline) return void 0;
|
|
4094
|
+
const change = { head: snap.head, branch: snap.branch, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4095
|
+
for (const listener of [...this.listeners]) {
|
|
4096
|
+
try {
|
|
4097
|
+
listener(change);
|
|
4098
|
+
} catch {
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
return change;
|
|
4102
|
+
} catch {
|
|
4103
|
+
return void 0;
|
|
4104
|
+
} finally {
|
|
4105
|
+
this.polling = false;
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
async snapshot() {
|
|
4109
|
+
const [statusRes, headRes, branch] = await Promise.all([
|
|
4110
|
+
runGit(["status", "--porcelain", "-z"], { cwd: this.root }),
|
|
4111
|
+
runGit(["rev-parse", "HEAD"], { cwd: this.root }).catch(() => ({ stdout: "" })),
|
|
4112
|
+
// The same label getRepoInfo reports, so the branch identity the client filters todos by
|
|
4113
|
+
// never flips between `HEAD` and a short sha depending on which of the two spoke last.
|
|
4114
|
+
currentBranch(this.root)
|
|
4115
|
+
]);
|
|
4116
|
+
const head = headRes.stdout.trim();
|
|
4117
|
+
const stats = await Promise.all(
|
|
4118
|
+
statusPaths(statusRes.stdout).map(async (rel) => {
|
|
4119
|
+
try {
|
|
4120
|
+
const st = await stat3(path5.join(this.root, rel));
|
|
4121
|
+
return `${rel} ${st.mtimeMs} ${st.size}`;
|
|
4122
|
+
} catch {
|
|
4123
|
+
return `${rel} -`;
|
|
4124
|
+
}
|
|
4125
|
+
})
|
|
4126
|
+
);
|
|
4127
|
+
return { signature: [head, branch, statusRes.stdout, ...stats].join("\n"), head, branch };
|
|
4128
|
+
}
|
|
4129
|
+
};
|
|
4130
|
+
|
|
4131
|
+
// packages/server/src/static.ts
|
|
4132
|
+
import path6 from "path";
|
|
4133
|
+
import { readFile as readFile3, stat as stat4 } from "fs/promises";
|
|
3784
4134
|
var MIME = {
|
|
3785
4135
|
".html": "text/html; charset=utf-8",
|
|
3786
4136
|
".js": "text/javascript; charset=utf-8",
|
|
@@ -3797,20 +4147,20 @@ var MIME = {
|
|
|
3797
4147
|
".txt": "text/plain; charset=utf-8"
|
|
3798
4148
|
};
|
|
3799
4149
|
async function serveStaticFile(c, webDir) {
|
|
3800
|
-
const root =
|
|
4150
|
+
const root = path6.resolve(webDir);
|
|
3801
4151
|
let rel;
|
|
3802
4152
|
try {
|
|
3803
4153
|
rel = decodeURIComponent(c.req.path);
|
|
3804
4154
|
} catch {
|
|
3805
4155
|
return c.text("Bad Request", 400);
|
|
3806
4156
|
}
|
|
3807
|
-
let target =
|
|
3808
|
-
if (target !== root && !target.startsWith(root +
|
|
4157
|
+
let target = path6.resolve(root, "." + rel);
|
|
4158
|
+
if (target !== root && !target.startsWith(root + path6.sep)) return c.text("Forbidden", 403);
|
|
3809
4159
|
try {
|
|
3810
|
-
const st = await
|
|
3811
|
-
if (st.isDirectory()) target =
|
|
4160
|
+
const st = await stat4(target);
|
|
4161
|
+
if (st.isDirectory()) target = path6.join(target, "index.html");
|
|
3812
4162
|
} catch {
|
|
3813
|
-
target =
|
|
4163
|
+
target = path6.join(root, "index.html");
|
|
3814
4164
|
}
|
|
3815
4165
|
let body;
|
|
3816
4166
|
try {
|
|
@@ -3818,7 +4168,7 @@ async function serveStaticFile(c, webDir) {
|
|
|
3818
4168
|
} catch {
|
|
3819
4169
|
return c.text("Not Found", 404);
|
|
3820
4170
|
}
|
|
3821
|
-
const ext =
|
|
4171
|
+
const ext = path6.extname(target).toLowerCase();
|
|
3822
4172
|
const isAsset = rel.startsWith("/assets/");
|
|
3823
4173
|
return new Response(new Uint8Array(body), {
|
|
3824
4174
|
status: 200,
|
|
@@ -3830,6 +4180,7 @@ async function serveStaticFile(c, webDir) {
|
|
|
3830
4180
|
}
|
|
3831
4181
|
|
|
3832
4182
|
// packages/server/src/app.ts
|
|
4183
|
+
var SSE_HEARTBEAT_MS = 15e3;
|
|
3833
4184
|
var LISTING_HINT_TTL_MS = 5 * 6e4;
|
|
3834
4185
|
function createApp(opts) {
|
|
3835
4186
|
const { repo, store } = opts;
|
|
@@ -3837,6 +4188,15 @@ function createApp(opts) {
|
|
|
3837
4188
|
const app = new Hono2();
|
|
3838
4189
|
const listings = /* @__PURE__ */ new Map();
|
|
3839
4190
|
const changedNotices = /* @__PURE__ */ new Map();
|
|
4191
|
+
const watchers = /* @__PURE__ */ new Map();
|
|
4192
|
+
const watcherFor = (root) => {
|
|
4193
|
+
let w = watchers.get(root);
|
|
4194
|
+
if (!w) {
|
|
4195
|
+
w = new RepoWatcher(root, opts.watchIntervalMs);
|
|
4196
|
+
watchers.set(root, w);
|
|
4197
|
+
}
|
|
4198
|
+
return w;
|
|
4199
|
+
};
|
|
3840
4200
|
let worktreeCache = null;
|
|
3841
4201
|
const worktrees = async (force = false) => {
|
|
3842
4202
|
if (!force && worktreeCache && Date.now() - worktreeCache.at < 1e4) return worktreeCache.list;
|
|
@@ -3898,6 +4258,7 @@ function createApp(opts) {
|
|
|
3898
4258
|
const prefs = await store.update((s) => {
|
|
3899
4259
|
if (body.viewMode === "unified" || body.viewMode === "split") s.prefs.viewMode = body.viewMode;
|
|
3900
4260
|
if (typeof body.lastTarget === "string") s.prefs.lastTarget = body.lastTarget;
|
|
4261
|
+
if (typeof body.autoRefresh === "boolean") s.prefs.autoRefresh = body.autoRefresh;
|
|
3901
4262
|
if (body.nvimSocketByRoot && typeof body.nvimSocketByRoot === "object") {
|
|
3902
4263
|
s.prefs.nvimSocketByRoot = { ...s.prefs.nvimSocketByRoot, ...body.nvimSocketByRoot };
|
|
3903
4264
|
}
|
|
@@ -3937,7 +4298,8 @@ function createApp(opts) {
|
|
|
3937
4298
|
viewed: !stale.includes(d.path) && viewed[d.path] === d.contentHash,
|
|
3938
4299
|
changed: notices.has(d.path)
|
|
3939
4300
|
}));
|
|
3940
|
-
const
|
|
4301
|
+
const scope = state.targets[commentScopeKey(key)];
|
|
4302
|
+
const res = { targetKey: key, root: ctx.cwd, files, comments: scope?.comments ?? [] };
|
|
3941
4303
|
return c.json(res);
|
|
3942
4304
|
});
|
|
3943
4305
|
api.get("/targets/:key/file", async (c) => {
|
|
@@ -3978,7 +4340,7 @@ function createApp(opts) {
|
|
|
3978
4340
|
api.get("/targets/:key/comments", async (c) => {
|
|
3979
4341
|
const key = decodeKey(c.req.param("key"));
|
|
3980
4342
|
const state = await store.load();
|
|
3981
|
-
return c.json({ comments: state.targets[key]?.comments ?? [] });
|
|
4343
|
+
return c.json({ comments: state.targets[commentScopeKey(key)]?.comments ?? [] });
|
|
3982
4344
|
});
|
|
3983
4345
|
api.post("/targets/:key/comments", async (c) => {
|
|
3984
4346
|
const key = decodeKey(c.req.param("key"));
|
|
@@ -4009,7 +4371,7 @@ function createApp(opts) {
|
|
|
4009
4371
|
updatedAt: now
|
|
4010
4372
|
};
|
|
4011
4373
|
await store.update((s) => {
|
|
4012
|
-
ensureTarget(s, key).comments.push(comment);
|
|
4374
|
+
ensureTarget(s, commentScopeKey(key)).comments.push(comment);
|
|
4013
4375
|
});
|
|
4014
4376
|
return c.json(comment, 201);
|
|
4015
4377
|
});
|
|
@@ -4018,8 +4380,9 @@ function createApp(opts) {
|
|
|
4018
4380
|
const id = c.req.param("id");
|
|
4019
4381
|
const ctx = await targetCtx(key);
|
|
4020
4382
|
const body = await c.req.json();
|
|
4383
|
+
const scope = commentScopeKey(key);
|
|
4021
4384
|
const state = await store.load();
|
|
4022
|
-
const existing = state.targets[
|
|
4385
|
+
const existing = state.targets[scope]?.comments.find((x) => x.id === id);
|
|
4023
4386
|
if (!existing) throw notFound("comment not found");
|
|
4024
4387
|
let patch = {};
|
|
4025
4388
|
if (typeof body.body === "string") {
|
|
@@ -4042,11 +4405,13 @@ function createApp(opts) {
|
|
|
4042
4405
|
endLine: anchored.endLine,
|
|
4043
4406
|
codeSnippet: anchored.snippet,
|
|
4044
4407
|
anchor: anchored.anchor,
|
|
4045
|
-
status: existing.exportedAt ? "exported" : "active"
|
|
4408
|
+
status: existing.exportedAt ? "exported" : "active",
|
|
4409
|
+
// Re-attaching in a view moves the comment to it.
|
|
4410
|
+
targetKey: key
|
|
4046
4411
|
};
|
|
4047
4412
|
}
|
|
4048
4413
|
const updated = await store.update((s) => {
|
|
4049
|
-
const t = ensureTarget(s,
|
|
4414
|
+
const t = ensureTarget(s, scope);
|
|
4050
4415
|
const idx = t.comments.findIndex((x) => x.id === id);
|
|
4051
4416
|
if (idx < 0) throw notFound("comment not found");
|
|
4052
4417
|
const next = { ...t.comments[idx], ...patch, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -4059,7 +4424,7 @@ function createApp(opts) {
|
|
|
4059
4424
|
const key = decodeKey(c.req.param("key"));
|
|
4060
4425
|
const id = c.req.param("id");
|
|
4061
4426
|
const removed = await store.update((s) => {
|
|
4062
|
-
const t = s.targets[key];
|
|
4427
|
+
const t = s.targets[commentScopeKey(key)];
|
|
4063
4428
|
if (!t) return false;
|
|
4064
4429
|
const before = t.comments.length;
|
|
4065
4430
|
t.comments = t.comments.filter((x) => x.id !== id);
|
|
@@ -4072,30 +4437,88 @@ function createApp(opts) {
|
|
|
4072
4437
|
api.post("/targets/:key/comments/reanchor", async (c) => {
|
|
4073
4438
|
const key = decodeKey(c.req.param("key"));
|
|
4074
4439
|
const ctx = await targetCtx(key);
|
|
4440
|
+
const scope = commentScopeKey(key);
|
|
4441
|
+
const local = isLocalTarget(ctx.target);
|
|
4075
4442
|
const body = await c.req.json().catch(() => ({})) ?? {};
|
|
4076
4443
|
const provided = /* @__PURE__ */ new Map();
|
|
4077
4444
|
for (const f of body.files ?? []) if (f && typeof f.path === "string" && Array.isArray(f.hunks)) provided.set(f.path, f);
|
|
4078
|
-
const
|
|
4079
|
-
const
|
|
4080
|
-
const paths = [...new Set(comments.map((x) => x.filePath))];
|
|
4445
|
+
const views = local ? localViewKeys(key) : [key];
|
|
4446
|
+
const contexts = /* @__PURE__ */ new Map([[key, ctx]]);
|
|
4081
4447
|
const diffs = /* @__PURE__ */ new Map();
|
|
4448
|
+
const diffFor = (view, filePath) => {
|
|
4449
|
+
let byPath = diffs.get(view);
|
|
4450
|
+
if (!byPath) {
|
|
4451
|
+
byPath = /* @__PURE__ */ new Map();
|
|
4452
|
+
diffs.set(view, byPath);
|
|
4453
|
+
}
|
|
4454
|
+
let pending = byPath.get(filePath);
|
|
4455
|
+
if (!pending) {
|
|
4456
|
+
pending = (async () => {
|
|
4457
|
+
if (view === key && provided.has(filePath)) return provided.get(filePath);
|
|
4458
|
+
try {
|
|
4459
|
+
let viewCtx = contexts.get(view);
|
|
4460
|
+
if (!viewCtx) {
|
|
4461
|
+
viewCtx = await targetCtx(view);
|
|
4462
|
+
contexts.set(view, viewCtx);
|
|
4463
|
+
}
|
|
4464
|
+
return await fileDiffWithHints(viewCtx, filePath);
|
|
4465
|
+
} catch {
|
|
4466
|
+
return void 0;
|
|
4467
|
+
}
|
|
4468
|
+
})();
|
|
4469
|
+
byPath.set(filePath, pending);
|
|
4470
|
+
}
|
|
4471
|
+
return pending;
|
|
4472
|
+
};
|
|
4473
|
+
const state = await store.load();
|
|
4474
|
+
const comments = state.targets[scope]?.comments ?? [];
|
|
4475
|
+
const prevHead = state.targets[scope]?.head;
|
|
4476
|
+
const head = await revParse(ctx.cwd, "HEAD") ?? "";
|
|
4477
|
+
const committed = local && prevHead !== void 0 && prevHead !== head;
|
|
4478
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4479
|
+
const located = /* @__PURE__ */ new Map();
|
|
4082
4480
|
await Promise.all(
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4481
|
+
comments.map(async (cm) => {
|
|
4482
|
+
const ordered = views.includes(cm.targetKey) ? [cm.targetKey, ...views.filter((v) => v !== cm.targetKey)] : views;
|
|
4483
|
+
for (const view of ordered) {
|
|
4484
|
+
const diff = await diffFor(view, cm.filePath);
|
|
4485
|
+
if (!diff) continue;
|
|
4486
|
+
const next = reanchorComment(cm, diff, now);
|
|
4487
|
+
if (next.status === "orphaned") continue;
|
|
4488
|
+
located.set(cm.id, {
|
|
4489
|
+
side: next.side,
|
|
4490
|
+
startLine: next.startLine,
|
|
4491
|
+
endLine: next.endLine,
|
|
4492
|
+
codeSnippet: next.codeSnippet,
|
|
4493
|
+
anchor: next.anchor,
|
|
4494
|
+
status: next.status,
|
|
4495
|
+
targetKey: view
|
|
4496
|
+
});
|
|
4086
4497
|
return;
|
|
4087
4498
|
}
|
|
4088
|
-
try {
|
|
4089
|
-
diffs.set(p, await fileDiffWithHints(ctx, p));
|
|
4090
|
-
} catch {
|
|
4091
|
-
diffs.set(p, void 0);
|
|
4092
|
-
}
|
|
4093
4499
|
})
|
|
4094
4500
|
);
|
|
4095
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4096
4501
|
const result = await store.update((s) => {
|
|
4097
|
-
const t = ensureTarget(s,
|
|
4098
|
-
|
|
4502
|
+
const t = ensureTarget(s, scope);
|
|
4503
|
+
const dropped = [];
|
|
4504
|
+
t.comments = t.comments.flatMap((cm) => {
|
|
4505
|
+
const patch = located.get(cm.id);
|
|
4506
|
+
if (!patch) {
|
|
4507
|
+
if (!comments.some((x) => x.id === cm.id)) return [cm];
|
|
4508
|
+
if (committed) {
|
|
4509
|
+
dropped.push(cm.id);
|
|
4510
|
+
return [];
|
|
4511
|
+
}
|
|
4512
|
+
return [cm.status === "orphaned" ? cm : { ...cm, status: "orphaned", updatedAt: now }];
|
|
4513
|
+
}
|
|
4514
|
+
const next = { ...cm, ...patch };
|
|
4515
|
+
const unchanged = cm.targetKey === next.targetKey && cm.status === next.status && cm.startLine === next.startLine && cm.endLine === next.endLine && cm.anchor.hunkHash === next.anchor.hunkHash;
|
|
4516
|
+
return [unchanged ? cm : { ...next, updatedAt: now }];
|
|
4517
|
+
});
|
|
4518
|
+
for (const id of dropped) {
|
|
4519
|
+
for (const issue of s.issues) issue.commentIds = issue.commentIds.filter((x) => x !== id);
|
|
4520
|
+
}
|
|
4521
|
+
t.head = head;
|
|
4099
4522
|
return t.comments;
|
|
4100
4523
|
});
|
|
4101
4524
|
const res = { comments: result };
|
|
@@ -4194,6 +4617,73 @@ function createApp(opts) {
|
|
|
4194
4617
|
});
|
|
4195
4618
|
return c.json(res);
|
|
4196
4619
|
});
|
|
4620
|
+
const knownRoot = async (rootParam) => {
|
|
4621
|
+
if (!rootParam) return repo.root;
|
|
4622
|
+
if (rootParam === repo.root || rootParam === repo.commonRoot) return rootParam;
|
|
4623
|
+
if ((await worktrees()).some((w) => w.path === rootParam)) return rootParam;
|
|
4624
|
+
throw badRequest(`unknown root: ${rootParam}`, "unknown_worktree");
|
|
4625
|
+
};
|
|
4626
|
+
api.get("/todos", async (c) => {
|
|
4627
|
+
const branch = c.req.query("branch") || void 0;
|
|
4628
|
+
const s = await store.load();
|
|
4629
|
+
const res = {
|
|
4630
|
+
todos: branch ? s.todos.filter((t) => t.branch === branch) : s.todos,
|
|
4631
|
+
...branch ? { branch } : {}
|
|
4632
|
+
};
|
|
4633
|
+
return c.json(res);
|
|
4634
|
+
});
|
|
4635
|
+
api.post("/todos", async (c) => {
|
|
4636
|
+
const body = await c.req.json();
|
|
4637
|
+
if (!body.title || !body.title.trim()) throw badRequest("title is required");
|
|
4638
|
+
const branch = body.branch?.trim() || await currentBranch(await knownRoot(body.root));
|
|
4639
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4640
|
+
const todo = {
|
|
4641
|
+
id: randomUUID(),
|
|
4642
|
+
branch,
|
|
4643
|
+
title: body.title.trim(),
|
|
4644
|
+
body: typeof body.body === "string" ? body.body : "",
|
|
4645
|
+
status: "open",
|
|
4646
|
+
createdAt: now,
|
|
4647
|
+
updatedAt: now
|
|
4648
|
+
};
|
|
4649
|
+
await store.update((s) => {
|
|
4650
|
+
s.todos.push(todo);
|
|
4651
|
+
});
|
|
4652
|
+
return c.json(todo, 201);
|
|
4653
|
+
});
|
|
4654
|
+
api.post("/todos/export", async (c) => {
|
|
4655
|
+
const body = await c.req.json();
|
|
4656
|
+
const branch = typeof body.branch === "string" ? body.branch.trim() : "";
|
|
4657
|
+
if (!branch) throw badRequest("branch is required");
|
|
4658
|
+
const s = await store.load();
|
|
4659
|
+
const todos = s.todos.filter((t) => t.branch === branch && (body.includeDone === true || t.status === "open"));
|
|
4660
|
+
const res = { text: formatTodosExport({ repoRoot: repo.root, branch, todos }), count: todos.length };
|
|
4661
|
+
return c.json(res);
|
|
4662
|
+
});
|
|
4663
|
+
api.patch("/todos/:id", async (c) => {
|
|
4664
|
+
const id = c.req.param("id");
|
|
4665
|
+
const body = await c.req.json();
|
|
4666
|
+
const updated = await store.update((s) => {
|
|
4667
|
+
const todo = s.todos.find((t) => t.id === id);
|
|
4668
|
+
if (!todo) throw notFound("todo not found");
|
|
4669
|
+
if (typeof body.title === "string" && body.title.trim()) todo.title = body.title.trim();
|
|
4670
|
+
if (typeof body.body === "string") todo.body = body.body;
|
|
4671
|
+
if (body.status === "open" || body.status === "done") todo.status = body.status;
|
|
4672
|
+
todo.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4673
|
+
return todo;
|
|
4674
|
+
});
|
|
4675
|
+
return c.json(updated);
|
|
4676
|
+
});
|
|
4677
|
+
api.delete("/todos/:id", async (c) => {
|
|
4678
|
+
const id = c.req.param("id");
|
|
4679
|
+
const removed = await store.update((s) => {
|
|
4680
|
+
const before = s.todos.length;
|
|
4681
|
+
s.todos = s.todos.filter((t) => t.id !== id);
|
|
4682
|
+
return s.todos.length !== before;
|
|
4683
|
+
});
|
|
4684
|
+
if (!removed) throw notFound("todo not found");
|
|
4685
|
+
return c.json({ ok: true });
|
|
4686
|
+
});
|
|
4197
4687
|
api.get("/commits", async (c) => {
|
|
4198
4688
|
const limitRaw = Number(c.req.query("limit") ?? 200);
|
|
4199
4689
|
const limit = Math.min(Math.max(Number.isFinite(limitRaw) ? Math.floor(limitRaw) : 200, 1), 1e3);
|
|
@@ -4231,6 +4721,33 @@ function createApp(opts) {
|
|
|
4231
4721
|
const res = { commits: commits.slice(0, limit), hasMore };
|
|
4232
4722
|
return c.json(res);
|
|
4233
4723
|
});
|
|
4724
|
+
api.get("/events", async (c) => {
|
|
4725
|
+
const root = c.req.query("root") || repo.root;
|
|
4726
|
+
const known = root === repo.root || root === repo.commonRoot || (await worktrees()).some((w) => w.path === root);
|
|
4727
|
+
if (!known) throw badRequest(`unknown root: ${root}`, "unknown_worktree");
|
|
4728
|
+
return streamSSE(c, async (stream2) => {
|
|
4729
|
+
const watcher = watcherFor(root);
|
|
4730
|
+
let chain = Promise.resolve();
|
|
4731
|
+
const enqueue = (write) => {
|
|
4732
|
+
chain = chain.then(write).catch(() => stream2.abort());
|
|
4733
|
+
return chain;
|
|
4734
|
+
};
|
|
4735
|
+
const release = watcher.subscribe((change) => {
|
|
4736
|
+
const event = { type: "changed", ...change };
|
|
4737
|
+
void enqueue(() => stream2.writeSSE({ event: "changed", data: JSON.stringify(event) }));
|
|
4738
|
+
});
|
|
4739
|
+
stream2.onAbort(release);
|
|
4740
|
+
try {
|
|
4741
|
+
while (!stream2.aborted && !stream2.closed) {
|
|
4742
|
+
await stream2.sleep(SSE_HEARTBEAT_MS);
|
|
4743
|
+
if (stream2.aborted || stream2.closed) break;
|
|
4744
|
+
await enqueue(() => stream2.write(": ping\n\n"));
|
|
4745
|
+
}
|
|
4746
|
+
} finally {
|
|
4747
|
+
release();
|
|
4748
|
+
}
|
|
4749
|
+
});
|
|
4750
|
+
});
|
|
4234
4751
|
api.get("/nvim/instances", async (c) => {
|
|
4235
4752
|
const root = c.req.query("root") || repo.root;
|
|
4236
4753
|
const force = c.req.query("rescan") === "1";
|
|
@@ -4255,7 +4772,7 @@ function createApp(opts) {
|
|
|
4255
4772
|
api.post("/nvim/open", async (c) => {
|
|
4256
4773
|
const body = await c.req.json();
|
|
4257
4774
|
if (!body.socket || !body.absPath) throw badRequest("socket and absPath are required");
|
|
4258
|
-
if (!
|
|
4775
|
+
if (!path7.isAbsolute(body.absPath)) throw badRequest("absPath must be absolute");
|
|
4259
4776
|
const scan = await nvim.scan();
|
|
4260
4777
|
if (!scan.instances.some((i) => i.socket === body.socket)) {
|
|
4261
4778
|
const fresh = await nvim.scan(true);
|
|
@@ -4329,7 +4846,7 @@ async function startServer(opts) {
|
|
|
4329
4846
|
}
|
|
4330
4847
|
|
|
4331
4848
|
// bin/cli.ts
|
|
4332
|
-
var VERSION = true ? "0.
|
|
4849
|
+
var VERSION = true ? "0.2.0" : "dev";
|
|
4333
4850
|
function parseArgs(argv) {
|
|
4334
4851
|
const args = { repoPath: process.cwd(), open: true, help: false, version: false };
|
|
4335
4852
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -4347,7 +4864,7 @@ function parseArgs(argv) {
|
|
|
4347
4864
|
} else if (a === "--help" || a === "-h") args.help = true;
|
|
4348
4865
|
else if (a === "--version" || a === "-v") args.version = true;
|
|
4349
4866
|
else if (a.startsWith("-")) throw new Error(`unknown option: ${a}`);
|
|
4350
|
-
else args.repoPath =
|
|
4867
|
+
else args.repoPath = path8.resolve(a);
|
|
4351
4868
|
}
|
|
4352
4869
|
return args;
|
|
4353
4870
|
}
|
|
@@ -4389,16 +4906,16 @@ async function openBrowser(url) {
|
|
|
4389
4906
|
return false;
|
|
4390
4907
|
}
|
|
4391
4908
|
function resolveWebDir() {
|
|
4392
|
-
const here =
|
|
4909
|
+
const here = path8.dirname(fileURLToPath(import.meta.url));
|
|
4393
4910
|
const candidates = [
|
|
4394
4911
|
process.env.WARDEN_WEB_DIR,
|
|
4395
|
-
|
|
4912
|
+
path8.join(here, "web"),
|
|
4396
4913
|
// dist/cli.js -> dist/web
|
|
4397
|
-
|
|
4914
|
+
path8.join(here, "..", "dist", "web")
|
|
4398
4915
|
// bin/cli.ts (tsx dev) -> dist/web
|
|
4399
4916
|
].filter((p) => !!p);
|
|
4400
4917
|
for (const dir of candidates) {
|
|
4401
|
-
if (existsSync(
|
|
4918
|
+
if (existsSync(path8.join(dir, "index.html"))) return dir;
|
|
4402
4919
|
}
|
|
4403
4920
|
return void 0;
|
|
4404
4921
|
}
|