@karmaniverous/jeeves-server 3.4.2 → 3.5.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/.tsbuildinfo +1 -1
- package/CHANGELOG.md +38 -1
- package/README.md +18 -17
- package/client/package.json +19 -19
- package/client/src/components/SearchModal.tsx +11 -1
- package/client/src/components/layout/Header.tsx +3 -3
- package/client/src/lib/api.ts +10 -5
- package/dist/client/assets/CodeEditor-Brh86AGF.js +1 -0
- package/dist/client/assets/CodeViewer-Cegj3cEn.js +1 -0
- package/dist/client/assets/dist-2YqVIvgv.js +2 -0
- package/dist/client/assets/dist-5vamY028.js +1 -0
- package/dist/client/assets/dist-6_auAGci.js +1 -0
- package/dist/client/assets/dist-B0kq1DQG.js +1 -0
- package/dist/client/assets/dist-B2SZD_eN.js +1 -0
- package/dist/client/assets/dist-B2t4dYA2.js +1 -0
- package/dist/client/assets/dist-B5gFYAn7.js +1 -0
- package/dist/client/assets/dist-BPy6CnYN.js +1 -0
- package/dist/client/assets/dist-CL6VCrQn.js +9 -0
- package/dist/client/assets/dist-CWsHar9N.js +1 -0
- package/dist/client/assets/dist-CnFc5Ssx.js +1 -0
- package/dist/client/assets/dist-DSgLBuTS.js +1 -0
- package/dist/client/assets/dist-DUcac0X_.js +7 -0
- package/dist/client/assets/dist-DcTcc-BG.js +6 -0
- package/dist/client/assets/dist-DvfTyWk_.js +1 -0
- package/dist/client/assets/dist-Dz1Ulpqa.js +1 -0
- package/dist/client/assets/dist-Kr-mUYW1.js +5 -0
- package/dist/client/assets/dist-OX4k3MMG.js +2 -0
- package/dist/client/assets/dist-qiU0qoeK.js +1 -0
- package/dist/client/assets/dist-ui4J6fvl.js +23 -0
- package/dist/client/assets/index-Dk_myGs4.css +2 -0
- package/dist/client/assets/index-DrBXupPz.js +62 -0
- package/dist/client/assets/theme-CPpIxvB0.js +2 -0
- package/dist/client/index.html +3 -2
- package/dist/src/cli/commands/config.test.js +5 -40
- package/dist/src/cli/index.js +9 -15
- package/dist/src/cli/start-server.js +16 -0
- package/dist/src/config/index.js +48 -37
- package/dist/src/config/loadConfig.test.js +27 -25
- package/dist/src/config/migration.js +60 -0
- package/dist/src/config/schema.js +4 -3
- package/dist/src/descriptor.js +46 -0
- package/dist/src/routes/api/diagramExport.js +101 -0
- package/dist/src/routes/api/diagramExport.test.js +134 -0
- package/dist/src/routes/api/events.js +13 -0
- package/dist/src/routes/api/export.js +6 -82
- package/dist/src/routes/api/index.js +4 -0
- package/dist/src/routes/api/search.js +9 -50
- package/dist/src/routes/api/sharing.js +40 -23
- package/dist/src/routes/api/sharing.test.js +52 -0
- package/dist/src/routes/auth.js +1 -1
- package/dist/src/routes/config.js +8 -2
- package/dist/src/routes/keys.js +4 -4
- package/dist/src/routes/path/index.js +1 -1
- package/dist/src/routes/status.js +15 -16
- package/dist/src/routes/status.test.js +13 -8
- package/dist/src/server.js +21 -16
- package/dist/src/services/markdown.js +2 -1
- package/dist/src/services/markdown.test.js +22 -0
- package/dist/src/util/packageVersion.js +7 -16
- package/dist/src/util/packageVersion.test.js +7 -0
- package/guides/api-integration.md +4 -0
- package/guides/deployment.md +11 -10
- package/guides/event-gateway.md +4 -0
- package/guides/exports.md +4 -0
- package/guides/index.md +1 -1
- package/guides/setup.md +17 -16
- package/guides/sharing.md +4 -0
- package/package.json +3 -3
- package/scripts/download-plantuml.js +0 -1
- package/src/cli/commands/config.test.ts +5 -45
- package/src/cli/index.ts +9 -16
- package/src/cli/start-server.ts +21 -0
- package/src/config/index.ts +56 -43
- package/src/config/loadConfig.test.ts +27 -29
- package/src/config/migration.ts +76 -0
- package/src/config/schema.ts +5 -4
- package/src/descriptor.ts +55 -0
- package/src/routes/api/diagramExport.test.ts +200 -0
- package/src/routes/api/diagramExport.ts +170 -0
- package/src/routes/api/events.ts +22 -0
- package/src/routes/api/export.ts +6 -131
- package/src/routes/api/index.ts +4 -0
- package/src/routes/api/search.ts +9 -63
- package/src/routes/api/sharing.test.ts +66 -0
- package/src/routes/api/sharing.ts +47 -23
- package/src/routes/auth.ts +1 -1
- package/src/routes/config.ts +15 -2
- package/src/routes/keys.ts +4 -4
- package/src/routes/path/index.ts +1 -1
- package/src/routes/status.test.ts +14 -8
- package/src/routes/status.ts +56 -62
- package/src/server.ts +29 -17
- package/src/services/markdown.test.ts +26 -0
- package/src/services/markdown.ts +2 -1
- package/src/util/packageVersion.test.ts +9 -0
- package/src/util/packageVersion.ts +11 -18
- package/src/util/platform.ts +1 -1
- package/dist/client/assets/CodeEditor-DQZZL5Rq.js +0 -1
- package/dist/client/assets/CodeViewer-ofJVD1Vn.js +0 -1
- package/dist/client/assets/index--MBieNJA.js +0 -1
- package/dist/client/assets/index-BENeXQI_.js +0 -1
- package/dist/client/assets/index-BbBpoOxz.js +0 -1
- package/dist/client/assets/index-BdV9g5AM.js +0 -6
- package/dist/client/assets/index-BjAilRri.js +0 -2
- package/dist/client/assets/index-BqbhWo2I.js +0 -3
- package/dist/client/assets/index-CVbycZ0H.js +0 -1
- package/dist/client/assets/index-Cs5oz2oJ.js +0 -5
- package/dist/client/assets/index-D-RC7ZS6.css +0 -1
- package/dist/client/assets/index-D8KZVveX.js +0 -1
- package/dist/client/assets/index-DC4HMHxY.js +0 -13
- package/dist/client/assets/index-DcY2RXqX.js +0 -1
- package/dist/client/assets/index-Duy-tZYV.js +0 -1
- package/dist/client/assets/index-Dw7rDFmE.js +0 -7
- package/dist/client/assets/index-FlCUvrjv.js +0 -2
- package/dist/client/assets/index-K6OVmfhg.js +0 -1
- package/dist/client/assets/index-MLwyFRN0.js +0 -1
- package/dist/client/assets/index-OpqBpSjn.js +0 -1
- package/dist/client/assets/index-SsHei0HE.js +0 -1
- package/dist/client/assets/index-jSGuHSeS.js +0 -62
- package/dist/client/assets/index-uQa2yckk.js +0 -1
- package/dist/client/assets/index-udkXoIER.js +0 -1
- package/dist/src/cli/commands/config.js +0 -105
- package/dist/src/cli/commands/service.js +0 -93
- package/dist/src/cli/commands/start.js +0 -24
- package/src/cli/commands/config.ts +0 -117
- package/src/cli/commands/service.ts +0 -129
- package/src/cli/commands/start.ts +0 -27
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Sharing API routes.
|
|
3
3
|
*
|
|
4
4
|
* Handles: /api/share, /api/util/share-for, /api/readme-link, /api/rotate-key
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
9
|
import crypto from 'node:crypto';
|
|
@@ -9,7 +11,7 @@ import fs from 'node:fs';
|
|
|
9
11
|
import path from 'node:path';
|
|
10
12
|
import { fileURLToPath } from 'node:url';
|
|
11
13
|
|
|
12
|
-
import type { FastifyPluginAsync } from 'fastify';
|
|
14
|
+
import type { FastifyPluginAsync, FastifyReply } from 'fastify';
|
|
13
15
|
|
|
14
16
|
import { _pathMatchesScopes } from '../../auth/keys.js';
|
|
15
17
|
import { findInsider } from '../../auth/resolve.js';
|
|
@@ -24,20 +26,41 @@ import {
|
|
|
24
26
|
import { fsPathToUrl, getRoots } from '../../util/platform.js';
|
|
25
27
|
import { setInsiderKey } from '../../util/state.js';
|
|
26
28
|
|
|
29
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
30
|
+
const serverRoot = path.resolve(__dirname, '..', '..', '..');
|
|
31
|
+
|
|
32
|
+
/** Build a deep-share browse URL from its constituent parts. */
|
|
33
|
+
function buildDeepShareUrl(
|
|
34
|
+
targetPath: string,
|
|
35
|
+
key: string,
|
|
36
|
+
params: { depth: number; dirs: boolean; stack: string; exp?: string },
|
|
37
|
+
): string {
|
|
38
|
+
let url = `/browse${targetPath}?key=${key}&d=${String(params.depth)}&dirs=${params.dirs ? '1' : '0'}&s=${params.stack}`;
|
|
39
|
+
if (params.exp) url += `&exp=${params.exp}`;
|
|
40
|
+
return url;
|
|
41
|
+
}
|
|
42
|
+
|
|
27
43
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
28
44
|
export const sharingRoutes: FastifyPluginAsync = async (fastify) => {
|
|
29
45
|
const roots = getRoots(getConfig().roots);
|
|
30
46
|
|
|
47
|
+
/** Resolve the _internal key seed, or send a 503 error. */
|
|
48
|
+
function getInternalSeed(reply: FastifyReply): string | null {
|
|
49
|
+
const internalKey = getConfig().resolvedKeys.find(
|
|
50
|
+
(k) => k.name === '_internal',
|
|
51
|
+
);
|
|
52
|
+
if (!internalKey?.seed) {
|
|
53
|
+
void reply.code(503).send({ error: 'No _internal key configured' });
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return internalKey.seed;
|
|
57
|
+
}
|
|
58
|
+
|
|
31
59
|
// GET /api/readme-link
|
|
32
60
|
fastify.get('/api/readme-link', async (_request, reply) => {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
if (!internalKey?.seed)
|
|
36
|
-
return reply.code(503).send({ error: 'No _internal key configured' });
|
|
61
|
+
const seed = getInternalSeed(reply);
|
|
62
|
+
if (!seed) return;
|
|
37
63
|
|
|
38
|
-
const seed = internalKey.seed;
|
|
39
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
40
|
-
const serverRoot = path.resolve(__dirname, '..', '..', '..');
|
|
41
64
|
const readmePath = path.join(serverRoot, 'README.md');
|
|
42
65
|
if (!fs.existsSync(readmePath))
|
|
43
66
|
return reply.code(404).send({ error: 'README.md not found' });
|
|
@@ -46,25 +69,21 @@ export const sharingRoutes: FastifyPluginAsync = async (fastify) => {
|
|
|
46
69
|
const stack = encodeStack([urlPath]);
|
|
47
70
|
const deepParams = { depth: 2, dirs: false, stack, exp: undefined };
|
|
48
71
|
const key = computeDeepShareKey(seed, urlPath, deepParams);
|
|
49
|
-
const shareUrl = `/browse${urlPath}?key=${key}&d=2&dirs=0&s=${stack}`;
|
|
50
72
|
|
|
51
|
-
return reply.send({
|
|
73
|
+
return reply.send({
|
|
74
|
+
url: buildDeepShareUrl(urlPath, key, { depth: 2, dirs: false, stack }),
|
|
75
|
+
});
|
|
52
76
|
});
|
|
53
77
|
|
|
54
78
|
// GET /api/content-link/:file — share link for content/*.md (terms, privacy)
|
|
55
79
|
fastify.get('/api/content-link/:file', async (request, reply) => {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
if (!internalKey?.seed)
|
|
59
|
-
return reply.code(503).send({ error: 'No _internal key configured' });
|
|
80
|
+
const seed = getInternalSeed(reply);
|
|
81
|
+
if (!seed) return;
|
|
60
82
|
|
|
61
83
|
const { file } = request.params as { file: string };
|
|
62
84
|
if (!/^[\w-]+$/.test(file))
|
|
63
85
|
return reply.code(400).send({ error: 'Invalid file name' });
|
|
64
86
|
|
|
65
|
-
const seed = internalKey.seed;
|
|
66
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
67
|
-
const serverRoot = path.resolve(__dirname, '..', '..', '..');
|
|
68
87
|
const contentPath = path.join(serverRoot, 'content', `${file}.md`);
|
|
69
88
|
if (!fs.existsSync(contentPath))
|
|
70
89
|
return reply.code(404).send({ error: `${file}.md not found` });
|
|
@@ -73,9 +92,10 @@ export const sharingRoutes: FastifyPluginAsync = async (fastify) => {
|
|
|
73
92
|
const stack = encodeStack([urlPath]);
|
|
74
93
|
const deepParams = { depth: 0, dirs: false, stack, exp: undefined };
|
|
75
94
|
const key = computeDeepShareKey(seed, urlPath, deepParams);
|
|
76
|
-
const shareUrl = `/browse${urlPath}?key=${key}&d=0&dirs=0&s=${stack}`;
|
|
77
95
|
|
|
78
|
-
return reply.send({
|
|
96
|
+
return reply.send({
|
|
97
|
+
url: buildDeepShareUrl(urlPath, key, { depth: 0, dirs: false, stack }),
|
|
98
|
+
});
|
|
79
99
|
});
|
|
80
100
|
|
|
81
101
|
// POST /api/share
|
|
@@ -100,8 +120,12 @@ export const sharingRoutes: FastifyPluginAsync = async (fastify) => {
|
|
|
100
120
|
exp: expiry,
|
|
101
121
|
};
|
|
102
122
|
outsiderKey = computeDeepShareKey(seed, targetPath, deepParams);
|
|
103
|
-
shareUrl =
|
|
104
|
-
|
|
123
|
+
shareUrl = buildDeepShareUrl(targetPath, outsiderKey, {
|
|
124
|
+
depth: depth ?? 0,
|
|
125
|
+
dirs: dirs ?? false,
|
|
126
|
+
stack,
|
|
127
|
+
exp: expiry,
|
|
128
|
+
});
|
|
105
129
|
} else if (expiry) {
|
|
106
130
|
outsiderKey = computeOutsiderKeyWithExpiry(seed, targetPath, expiry);
|
|
107
131
|
shareUrl = `/browse${targetPath}?key=${outsiderKey}&exp=${expiry}`;
|
|
@@ -120,7 +144,7 @@ export const sharingRoutes: FastifyPluginAsync = async (fastify) => {
|
|
|
120
144
|
});
|
|
121
145
|
|
|
122
146
|
// POST /api/rotate-key
|
|
123
|
-
fastify.post('/api/rotate-key',
|
|
147
|
+
fastify.post('/api/rotate-key', (request, reply) => {
|
|
124
148
|
const insiderEmail = request.insiderEmail;
|
|
125
149
|
if (!insiderEmail)
|
|
126
150
|
return reply.code(403).send({ error: 'Insider auth required' });
|
|
@@ -132,7 +156,7 @@ export const sharingRoutes: FastifyPluginAsync = async (fastify) => {
|
|
|
132
156
|
const newSeed = crypto.randomBytes(32).toString('hex');
|
|
133
157
|
const now = new Date().toISOString();
|
|
134
158
|
setInsiderKey(insider.email, newSeed, now);
|
|
135
|
-
|
|
159
|
+
resetConfig();
|
|
136
160
|
|
|
137
161
|
return reply.send({ ok: true, keyCreatedAt: now });
|
|
138
162
|
});
|
package/src/routes/auth.ts
CHANGED
|
@@ -108,7 +108,7 @@ export const authRoute: FastifyPluginAsync = async (fastify) => {
|
|
|
108
108
|
|
|
109
109
|
// Persist to state.json (mutable runtime state)
|
|
110
110
|
setInsiderKey(insider.email, newSeed, timestamp);
|
|
111
|
-
|
|
111
|
+
resetConfig(); // Reload to pick up new state
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
// Set session cookie
|
package/src/routes/config.ts
CHANGED
|
@@ -6,11 +6,15 @@
|
|
|
6
6
|
* @packageDocumentation
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
createConfigApplyHandler,
|
|
11
|
+
createConfigQueryHandler,
|
|
12
|
+
} from '@karmaniverous/jeeves';
|
|
10
13
|
import type { FastifyInstance } from 'fastify';
|
|
11
14
|
|
|
12
15
|
import { getConfig } from '../config/index.js';
|
|
13
16
|
import type { RuntimeConfig } from '../config/types.js';
|
|
17
|
+
import { serverDescriptor } from '../descriptor.js';
|
|
14
18
|
|
|
15
19
|
/** Return a sanitized copy of the config (redact sensitive fields). */
|
|
16
20
|
export function sanitizeConfig(config: RuntimeConfig): unknown {
|
|
@@ -35,7 +39,7 @@ export function sanitizeConfig(config: RuntimeConfig): unknown {
|
|
|
35
39
|
};
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
/** Register the GET /config
|
|
42
|
+
/** Register the GET /config and POST /config/apply routes. */
|
|
39
43
|
export function registerConfigRoute(app: FastifyInstance): void {
|
|
40
44
|
const configHandler = createConfigQueryHandler(() =>
|
|
41
45
|
sanitizeConfig(getConfig()),
|
|
@@ -46,4 +50,13 @@ export function registerConfigRoute(app: FastifyInstance): void {
|
|
|
46
50
|
const result = await configHandler({ path });
|
|
47
51
|
return reply.status(result.status).send(result.body);
|
|
48
52
|
});
|
|
53
|
+
|
|
54
|
+
const applyHandler = createConfigApplyHandler(serverDescriptor);
|
|
55
|
+
|
|
56
|
+
app.post('/config/apply', async (request, reply) => {
|
|
57
|
+
const result = await applyHandler(
|
|
58
|
+
request.body as { patch: Record<string, unknown>; replace?: boolean },
|
|
59
|
+
);
|
|
60
|
+
return reply.status(result.status).send(result.body);
|
|
61
|
+
});
|
|
49
62
|
}
|
package/src/routes/keys.ts
CHANGED
|
@@ -86,7 +86,7 @@ export const keysRoute: FastifyPluginAsync = async (fastify) => {
|
|
|
86
86
|
const insiderResult = resolveInsiderKeyAuth(config, provided);
|
|
87
87
|
if (insiderResult.valid && insiderResult.email) {
|
|
88
88
|
// Insider key rotation
|
|
89
|
-
return
|
|
89
|
+
return rotateInsiderSeed(insiderResult.email, config);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
// Machine key rotation is not supported with TS config
|
|
@@ -105,7 +105,7 @@ export const keysRoute: FastifyPluginAsync = async (fastify) => {
|
|
|
105
105
|
// Try session-based auth
|
|
106
106
|
const sessionResult = resolveSessionAuth(config, request);
|
|
107
107
|
if (sessionResult.valid && sessionResult.email) {
|
|
108
|
-
return
|
|
108
|
+
return rotateInsiderSeed(sessionResult.email, config);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
return reply.code(401).send({ error: 'Invalid insider key' });
|
|
@@ -149,7 +149,7 @@ export const keysRoute: FastifyPluginAsync = async (fastify) => {
|
|
|
149
149
|
);
|
|
150
150
|
};
|
|
151
151
|
|
|
152
|
-
|
|
152
|
+
function rotateInsiderSeed(
|
|
153
153
|
email: string,
|
|
154
154
|
config: ReturnType<typeof getConfig>,
|
|
155
155
|
) {
|
|
@@ -166,7 +166,7 @@ async function rotateInsiderSeed(
|
|
|
166
166
|
at: timestamp,
|
|
167
167
|
});
|
|
168
168
|
setKeyRotationTimestamp(timestamp);
|
|
169
|
-
|
|
169
|
+
resetConfig();
|
|
170
170
|
|
|
171
171
|
return { ok: true, keyName: insider.email };
|
|
172
172
|
}
|
package/src/routes/path/index.ts
CHANGED
|
@@ -16,7 +16,7 @@ export const pathRoute: FastifyPluginAsync = async (fastify) => {
|
|
|
16
16
|
'/path/*',
|
|
17
17
|
async (request, reply) => {
|
|
18
18
|
const reqPath = request.params['*'];
|
|
19
|
-
const url = new URL(request.url, 'http://
|
|
19
|
+
const url = new URL(request.url, 'http://127.0.0.1');
|
|
20
20
|
const query = url.search;
|
|
21
21
|
return reply.redirect(`/browse/${reqPath}${query}`);
|
|
22
22
|
},
|
|
@@ -31,7 +31,7 @@ vi.mock('../config/index.js', () => ({
|
|
|
31
31
|
const { statusRoutes } = await import('./status.js');
|
|
32
32
|
|
|
33
33
|
describe('GET /status', () => {
|
|
34
|
-
it('returns structured status', async () => {
|
|
34
|
+
it('returns structured status with SDK shape', async () => {
|
|
35
35
|
// Create a minimal Fastify-like test harness
|
|
36
36
|
const routes: Record<string, (req: unknown) => Promise<unknown>> = {};
|
|
37
37
|
const fakeFastify = {
|
|
@@ -48,14 +48,20 @@ describe('GET /status', () => {
|
|
|
48
48
|
const result = await handler({ accessMode: 'insider', query: {} });
|
|
49
49
|
const status = result as Record<string, unknown>;
|
|
50
50
|
|
|
51
|
+
// Standard SDK fields at top level
|
|
52
|
+
expect(status).toHaveProperty('name', 'server');
|
|
51
53
|
expect(status).toHaveProperty('version');
|
|
52
54
|
expect(status).toHaveProperty('uptime');
|
|
53
|
-
expect(status
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
expect(
|
|
58
|
-
|
|
55
|
+
expect(status).toHaveProperty('status', 'healthy');
|
|
56
|
+
|
|
57
|
+
// Server-specific fields nested under health
|
|
58
|
+
const health = status.health as Record<string, unknown>;
|
|
59
|
+
expect(health.port).toBe(1934);
|
|
60
|
+
expect((health.chrome as { configured: boolean }).configured).toBe(true);
|
|
61
|
+
expect((health.auth as { insiderCount: number }).insiderCount).toBe(2);
|
|
62
|
+
expect((health.auth as { keyCount: number }).keyCount).toBe(1);
|
|
63
|
+
expect(health.events).toHaveLength(2);
|
|
64
|
+
const exports = health.exports as {
|
|
59
65
|
documents: string[];
|
|
60
66
|
directories: string[];
|
|
61
67
|
diagrams: string[];
|
|
@@ -65,6 +71,6 @@ describe('GET /status', () => {
|
|
|
65
71
|
expect(exports.directories).toEqual(['zip']);
|
|
66
72
|
expect(exports.diagrams).toEqual(['svg', 'png']);
|
|
67
73
|
expect(exports.chromeAvailable).toBe(true);
|
|
68
|
-
expect((
|
|
74
|
+
expect((health.diagrams as { mermaid: boolean }).mermaid).toBe(true);
|
|
69
75
|
});
|
|
70
76
|
});
|
package/src/routes/status.ts
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Server status endpoint —
|
|
2
|
+
* Server status endpoint — uses the SDK's `createStatusHandler` factory.
|
|
3
3
|
*
|
|
4
|
-
* Returns version, uptime,
|
|
5
|
-
*
|
|
4
|
+
* Returns standard `{ name, version, uptime, status, health }` shape
|
|
5
|
+
* with server-specific details nested under `health`.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { createStatusHandler } from '@karmaniverous/jeeves';
|
|
8
9
|
import type { FastifyPluginAsync } from 'fastify';
|
|
9
10
|
|
|
10
11
|
import { getConfig } from '../config/index.js';
|
|
11
|
-
import { getRecentEvents } from '../services/eventLog.js';
|
|
12
12
|
import { packageVersion } from '../util/packageVersion.js';
|
|
13
13
|
|
|
14
|
-
const startTime = Date.now();
|
|
15
|
-
|
|
16
14
|
interface ServiceStatus {
|
|
17
15
|
url: string;
|
|
18
16
|
reachable: boolean;
|
|
@@ -20,7 +18,6 @@ interface ServiceStatus {
|
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
async function checkService(url: string): Promise<ServiceStatus> {
|
|
23
|
-
// Try /status first (watcher), then /health (runner)
|
|
24
21
|
for (const endpoint of ['/status', '/health']) {
|
|
25
22
|
try {
|
|
26
23
|
const res = await fetch(`${url}${endpoint}`, {
|
|
@@ -37,62 +34,59 @@ async function checkService(url: string): Promise<ServiceStatus> {
|
|
|
37
34
|
return { url, reachable: false };
|
|
38
35
|
}
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const config = getConfig();
|
|
37
|
+
const handleStatus = createStatusHandler({
|
|
38
|
+
name: 'server',
|
|
39
|
+
version: packageVersion,
|
|
40
|
+
getHealth: async () => {
|
|
41
|
+
const config = getConfig();
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
const [watcher, runner, meta] = await Promise.all([
|
|
44
|
+
config.watcherUrl ? checkService(config.watcherUrl) : null,
|
|
45
|
+
config.runnerUrl ? checkService(config.runnerUrl) : null,
|
|
46
|
+
config.metaUrl ? checkService(config.metaUrl) : null,
|
|
47
|
+
]);
|
|
52
48
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
49
|
+
return {
|
|
50
|
+
port: config.port,
|
|
51
|
+
chrome: {
|
|
52
|
+
configured: Boolean(config.chromePath),
|
|
53
|
+
path: config.chromePath,
|
|
54
|
+
},
|
|
55
|
+
auth: {
|
|
56
|
+
modes: config.authModes,
|
|
57
|
+
insiderCount: config.resolvedInsiders.length,
|
|
58
|
+
keyCount: config.resolvedKeys.length,
|
|
59
|
+
},
|
|
60
|
+
events: Object.entries(config.events).map(([name, schema]) => ({
|
|
61
|
+
name,
|
|
62
|
+
cmd: schema.cmd,
|
|
63
|
+
})),
|
|
64
|
+
exports: {
|
|
65
|
+
documents: ['pdf', 'docx'],
|
|
66
|
+
directories: ['zip'],
|
|
67
|
+
diagrams: ['svg', 'png'],
|
|
68
|
+
chromeAvailable: Boolean(config.chromePath),
|
|
69
|
+
},
|
|
70
|
+
diagrams: {
|
|
71
|
+
mermaid: true,
|
|
72
|
+
plantuml: {
|
|
73
|
+
localJar: Boolean(config.plantuml.jarPath),
|
|
74
|
+
servers: config.plantuml.servers,
|
|
75
75
|
},
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
),
|
|
93
|
-
}
|
|
94
|
-
: {}),
|
|
95
|
-
};
|
|
96
|
-
},
|
|
97
|
-
);
|
|
76
|
+
},
|
|
77
|
+
services: {
|
|
78
|
+
watcher,
|
|
79
|
+
runner,
|
|
80
|
+
meta,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
87
|
+
export const statusRoutes: FastifyPluginAsync = async (fastify) => {
|
|
88
|
+
fastify.get('/status', async () => {
|
|
89
|
+
const result = await handleStatus();
|
|
90
|
+
return result.body;
|
|
91
|
+
});
|
|
98
92
|
};
|
package/src/server.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
9
9
|
|
|
10
10
|
import cookie from '@fastify/cookie';
|
|
11
11
|
import fastifyStatic from '@fastify/static';
|
|
12
|
-
import Fastify from 'fastify';
|
|
12
|
+
import Fastify, { type FastifyReply, type FastifyRequest } from 'fastify';
|
|
13
13
|
|
|
14
14
|
import { getConfig, initConfig, isConfigInitialized } from './config/index.js';
|
|
15
15
|
import { apiRoute } from './routes/api/index.js';
|
|
@@ -28,7 +28,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
28
28
|
|
|
29
29
|
async function start() {
|
|
30
30
|
try {
|
|
31
|
-
const config = isConfigInitialized() ? getConfig() :
|
|
31
|
+
const config = isConfigInitialized() ? getConfig() : initConfig();
|
|
32
32
|
|
|
33
33
|
const fastify = Fastify({
|
|
34
34
|
logger: true,
|
|
@@ -60,21 +60,33 @@ async function start() {
|
|
|
60
60
|
prefix: '/app/',
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
// SPA fallback for
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
63
|
+
// SPA fallback — all these routes serve index.html for client-side routing
|
|
64
|
+
const spaFallback = async (
|
|
65
|
+
_request: FastifyRequest,
|
|
66
|
+
reply: FastifyReply,
|
|
67
|
+
) => reply.sendFile('index.html', clientDir);
|
|
68
|
+
|
|
69
|
+
for (const route of [
|
|
70
|
+
'/',
|
|
71
|
+
'/browse',
|
|
72
|
+
'/browse/*',
|
|
73
|
+
'/runner',
|
|
74
|
+
'/runner/*',
|
|
75
|
+
]) {
|
|
76
|
+
fastify.get(route, spaFallback);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Catch-all: serve SPA for any unmatched GET under /browse or /runner
|
|
80
|
+
// (handles edge cases like dotfile paths that wildcard routes may miss)
|
|
81
|
+
fastify.setNotFoundHandler(async (request, reply) => {
|
|
82
|
+
if (
|
|
83
|
+
request.method === 'GET' &&
|
|
84
|
+
(request.url.startsWith('/browse') ||
|
|
85
|
+
request.url.startsWith('/runner'))
|
|
86
|
+
) {
|
|
87
|
+
return reply.sendFile('index.html', clientDir);
|
|
88
|
+
}
|
|
89
|
+
return reply.code(404).send({ error: 'Not found' });
|
|
78
90
|
});
|
|
79
91
|
}
|
|
80
92
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { parseMarkdown } from './markdown.js';
|
|
4
|
+
|
|
5
|
+
describe('parseMarkdown', () => {
|
|
6
|
+
it('decodes HTML entities in heading text for TOC', () => {
|
|
7
|
+
const md = '# Hello & "World"';
|
|
8
|
+
const { headings } = parseMarkdown(md);
|
|
9
|
+
expect(headings).toHaveLength(1);
|
|
10
|
+
expect(headings[0].text).toBe('Hello & "World"');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('strips HTML tags from heading text for TOC', () => {
|
|
14
|
+
const md = '## A <em>bold</em> heading';
|
|
15
|
+
const { headings } = parseMarkdown(md);
|
|
16
|
+
expect(headings).toHaveLength(1);
|
|
17
|
+
expect(headings[0].text).toBe('A bold heading');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('decodes ' and " entities in headings', () => {
|
|
21
|
+
const md = '### It's a "test"';
|
|
22
|
+
const { headings } = parseMarkdown(md);
|
|
23
|
+
expect(headings).toHaveLength(1);
|
|
24
|
+
expect(headings[0].text).toBe('It\'s a "test"');
|
|
25
|
+
});
|
|
26
|
+
});
|
package/src/services/markdown.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
|
|
7
|
+
import * as cheerio from 'cheerio';
|
|
7
8
|
import type { Token } from 'marked';
|
|
8
9
|
import { marked } from 'marked';
|
|
9
10
|
|
|
@@ -135,7 +136,7 @@ export function parseMarkdown(
|
|
|
135
136
|
|
|
136
137
|
headings.push({
|
|
137
138
|
level,
|
|
138
|
-
text: renderedText.
|
|
139
|
+
text: cheerio.load(renderedText).text(),
|
|
139
140
|
slug,
|
|
140
141
|
});
|
|
141
142
|
|
|
@@ -1,30 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Resolve the service package version
|
|
2
|
+
* Resolve the service package version using package-directory.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
let dir = startDir;
|
|
11
|
-
while (dir !== path.dirname(dir)) {
|
|
12
|
-
const candidate = path.join(dir, 'package.json');
|
|
13
|
-
if (fs.existsSync(candidate)) {
|
|
14
|
-
const pkg = JSON.parse(fs.readFileSync(candidate, 'utf8')) as {
|
|
15
|
-
name?: string;
|
|
16
|
-
};
|
|
17
|
-
// Find our package specifically, not the monorepo root
|
|
18
|
-
if (pkg.name === '@karmaniverous/jeeves-server') return candidate;
|
|
19
|
-
}
|
|
20
|
-
dir = path.dirname(dir);
|
|
21
|
-
}
|
|
22
|
-
throw new Error('Could not find @karmaniverous/jeeves-server package.json');
|
|
23
|
-
}
|
|
9
|
+
import { packageDirectorySync } from 'package-directory';
|
|
24
10
|
|
|
25
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
26
|
-
const
|
|
27
|
-
|
|
12
|
+
const pkgDir = packageDirectorySync({ cwd: __dirname });
|
|
13
|
+
if (!pkgDir) {
|
|
14
|
+
throw new Error('Could not find package directory for jeeves-server');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const pkgPath = path.join(pkgDir, 'package.json');
|
|
18
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')) as {
|
|
19
|
+
version: string;
|
|
20
|
+
};
|
|
28
21
|
|
|
29
22
|
/** The package version of the jeeves-server service package. */
|
|
30
23
|
export const packageVersion: string = pkg.version;
|
package/src/util/platform.ts
CHANGED
|
@@ -10,7 +10,7 @@ import path from 'node:path';
|
|
|
10
10
|
|
|
11
11
|
const IS_WINDOWS = process.platform === 'win32';
|
|
12
12
|
|
|
13
|
-
interface RootEntry {
|
|
13
|
+
export interface RootEntry {
|
|
14
14
|
/** URL-safe identifier (drive letter lowercase on Windows, mount name on Linux) */
|
|
15
15
|
id: string;
|
|
16
16
|
/** Display label */
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as t,u as L,l as M,g as B,j as e}from"./index-jSGuHSeS.js";function J({content:i,fileName:h,onSave:c,onCancel:b}){const n=t.useRef(null),r=t.useRef(null),[d,l]=t.useState(!1),[u,f]=t.useState(!1),[y,v]=t.useState(!0),[C]=L(),x=t.useRef(i),m=t.useCallback(async()=>{if(!r.current)return;const s=r.current.state.doc.toString();f(!0);try{await c(s),x.current=s,l(!1)}finally{f(!1)}},[c]);return t.useEffect(()=>{if(!n.current)return;let s=!1;return(async()=>{const{EditorView:o,EditorState:j,basicSetup:w,keymap:S,oneDark:N}=await M();if(s)return;const E=h.split(".").pop()??"",p=await B(E);if(s)return;const a=[w,S.of([{key:"Mod-s",run:()=>(m(),!0)}]),o.updateListener.of(g=>{if(g.docChanged){const F=g.state.doc.toString();l(F!==x.current)}}),o.theme({"&":{fontSize:"14px",height:"100%"},".cm-scroller":{overflow:"auto"},".cm-content":{fontFamily:"'JetBrains Mono', 'Fira Code', 'Consolas', monospace"},".cm-gutters":{fontFamily:"'JetBrains Mono', 'Fira Code', 'Consolas', monospace"}})];C==="dark"&&a.push(N),p&&a.push(p);const k=j.create({doc:i,extensions:a}),R=new o({state:k,parent:n.current});r.current=R,v(!1)})(),()=>{s=!0,r.current&&(r.current.destroy(),r.current=null)}},[]),e.jsxs("div",{className:"flex flex-col h-full",children:[e.jsxs("div",{className:"flex items-center gap-2 px-3 py-2 border-b border-border bg-muted/50",children:[e.jsx("span",{className:"text-sm font-medium text-foreground",children:"Editing"}),d&&e.jsx("span",{className:"text-xs px-1.5 py-0.5 bg-amber-500/20 text-amber-600 dark:text-amber-400 rounded",children:"unsaved"}),e.jsx("div",{className:"flex-1"}),e.jsx("button",{onClick:b,className:"px-3 py-1 text-sm rounded border border-border text-muted-foreground hover:bg-accent transition-colors",children:"Cancel"}),e.jsx("button",{onClick:m,disabled:u||!d,className:"px-3 py-1 text-sm rounded bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",children:u?"Saving…":"Save"}),e.jsx("span",{className:"text-xs text-muted-foreground hidden sm:inline",children:"Ctrl+S"})]}),e.jsx("div",{ref:n,className:"flex-1 overflow-hidden",children:y&&e.jsx("div",{className:"flex items-center justify-center h-32 text-muted-foreground",children:"Loading editor…"})})]})}export{J as CodeEditor};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{c as b,r as t,u as j,l as E,g as k,j as e,C as N}from"./index-jSGuHSeS.js";const R=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],S=b("copy",R);function M({content:o,fileName:i}){const s=t.useRef(null),r=t.useRef(null),[f,x]=t.useState(!0),[h,d]=t.useState(!1),[l]=j(),m=async()=>{await navigator.clipboard.writeText(o),d(!0),setTimeout(()=>d(!1),1500)};return t.useEffect(()=>{if(!s.current)return;let n=!1;return(async()=>{const{EditorView:a,EditorState:u,basicSetup:g,oneDark:y}=await E();if(n)return;const w=i.split(".").pop()??"",p=await k(w);if(n)return;const c=[g,u.readOnly.of(!0),a.editable.of(!1),a.theme({"&":{fontSize:"14px"},".cm-scroller":{overflow:"auto"},".cm-content":{fontFamily:"'JetBrains Mono', 'Fira Code', 'Consolas', monospace"},".cm-gutters":{fontFamily:"'JetBrains Mono', 'Fira Code', 'Consolas', monospace"},".cm-cursor":{display:"none"}})];l==="dark"&&c.push(y),p&&c.push(p);const v=u.create({doc:o,extensions:c}),C=new a({state:v,parent:s.current});r.current=C,x(!1)})(),()=>{n=!0,r.current&&(r.current.destroy(),r.current=null)}},[o,i,l]),e.jsxs("div",{className:"relative group rounded-lg border border-border overflow-hidden",children:[e.jsx("div",{className:"absolute top-2 right-2 z-10",children:e.jsx("button",{onClick:()=>{m()},className:"p-1.5 rounded bg-accent hover:bg-accent/80 text-muted-foreground hover:text-foreground opacity-0 group-hover:opacity-100 transition-all",title:"Copy to clipboard",children:h?e.jsx(N,{className:"h-3.5 w-3.5 text-green-400"}):e.jsx(S,{className:"h-3.5 w-3.5"})})}),e.jsx("div",{ref:s,children:f&&e.jsx("pre",{className:"p-4 text-sm text-muted-foreground bg-muted",children:e.jsxs("code",{children:[o.slice(0,200),"…"]})})})]})}export{M as CodeViewer};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{L as V,E as i,C as _}from"./index-K6OVmfhg.js";import{k as Z,t,L as w,l as L,n as B,D as v,p as E,q as M}from"./index-CVbycZ0H.js";import"./index-Cs5oz2oJ.js";import"./index-Dw7rDFmE.js";const f=63,q=64,j=1,A=2,U=3,H=4,W=5,N=6,I=7,y=65,u=66,F=8,K=9,J=10,OO=11,eO=12,Y=13,aO=19,rO=20,QO=29,tO=33,PO=34,oO=47,nO=0,$=1,b=2,d=3,g=4;class s{constructor(e,a,r){this.parent=e,this.depth=a,this.type=r,this.hash=(e?e.hash+e.hash<<8:0)+a+(a<<4)+r}}s.top=new s(null,-1,nO);function X(O,e){for(let a=0,r=e-O.pos-1;;r--,a++){let P=O.peek(r);if(o(P)||P==-1)return a}}function x(O){return O==32||O==9}function o(O){return O==10||O==13}function z(O){return x(O)||o(O)}function l(O){return O<0||z(O)}const sO=new _({start:s.top,reduce(O,e){return O.type==d&&(e==rO||e==PO)?O.parent:O},shift(O,e,a,r){if(e==U)return new s(O,X(r,r.pos),$);if(e==y||e==W)return new s(O,X(r,r.pos),b);if(e==f)return O.parent;if(e==aO||e==tO)return new s(O,0,d);if(e==Y&&O.type==g)return O.parent;if(e==oO){let P=/[1-9]/.exec(r.read(r.pos,a.pos));if(P)return new s(O,O.depth+ +P[0],g)}return O},hash(O){return O.hash}});function c(O,e,a=0){return O.peek(a)==e&&O.peek(a+1)==e&&O.peek(a+2)==e&&l(O.peek(a+3))}const lO=new i((O,e)=>{if(O.next==-1&&e.canShift(q))return O.acceptToken(q);let a=O.peek(-1);if((o(a)||a<0)&&e.context.type!=d){if(c(O,45))if(e.canShift(f))O.acceptToken(f);else return O.acceptToken(j,3);if(c(O,46))if(e.canShift(f))O.acceptToken(f);else return O.acceptToken(A,3);let r=0;for(;O.next==32;)r++,O.advance();(r<e.context.depth||r==e.context.depth&&e.context.type==$&&(O.next!=45||!l(O.peek(1))))&&O.next!=-1&&!o(O.next)&&O.next!=35&&O.acceptToken(f,-r)}},{contextual:!0}),fO=new i((O,e)=>{if(e.context.type==d){O.next==63&&(O.advance(),l(O.next)&&O.acceptToken(I));return}if(O.next==45)O.advance(),l(O.next)&&O.acceptToken(e.context.type==$&&e.context.depth==X(O,O.pos-1)?H:U);else if(O.next==63)O.advance(),l(O.next)&&O.acceptToken(e.context.type==b&&e.context.depth==X(O,O.pos-1)?N:W);else{let a=O.pos;for(;;)if(x(O.next)){if(O.pos==a)return;O.advance()}else if(O.next==33)C(O);else if(O.next==38)m(O);else if(O.next==42){m(O);break}else if(O.next==39||O.next==34){if(T(O,!0))break;return}else if(O.next==91||O.next==123){if(!XO(O))return;break}else{G(O,!0,!1,0);break}for(;x(O.next);)O.advance();if(O.next==58){if(O.pos==a&&e.canShift(QO))return;let r=O.peek(1);l(r)&&O.acceptTokenTo(e.context.type==b&&e.context.depth==X(O,a)?u:y,a)}}},{contextual:!0});function cO(O){return O>32&&O<127&&O!=34&&O!=37&&O!=44&&O!=60&&O!=62&&O!=92&&O!=94&&O!=96&&O!=123&&O!=124&&O!=125}function D(O){return O>=48&&O<=57||O>=97&&O<=102||O>=65&&O<=70}function p(O,e){return O.next==37?(O.advance(),D(O.next)&&O.advance(),D(O.next)&&O.advance(),!0):cO(O.next)||e&&O.next==44?(O.advance(),!0):!1}function C(O){if(O.advance(),O.next==60){for(O.advance();;)if(!p(O,!0)){O.next==62&&O.advance();break}}else for(;p(O,!1););}function m(O){for(O.advance();!l(O.next)&&S(O.next)!="f";)O.advance()}function T(O,e){let a=O.next,r=!1,P=O.pos;for(O.advance();;){let Q=O.next;if(Q<0)break;if(O.advance(),Q==a)if(Q==39)if(O.next==39)O.advance();else break;else break;else if(Q==92&&a==34)O.next>=0&&O.advance();else if(o(Q)){if(e)return!1;r=!0}else if(e&&O.pos>=P+1024)return!1}return!r}function XO(O){for(let e=[],a=O.pos+1024;;)if(O.next==91||O.next==123)e.push(O.next),O.advance();else if(O.next==39||O.next==34){if(!T(O,!0))return!1}else if(O.next==93||O.next==125){if(e[e.length-1]!=O.next-2)return!1;if(e.pop(),O.advance(),!e.length)return!0}else{if(O.next<0||O.pos>a||o(O.next))return!1;O.advance()}}const dO="iiisiiissisfissssssssssssisssiiissssssssssssssssssssssssssfsfssissssssssssssssssssssssssssfif";function S(O){return O<33?"u":O>125?"s":dO[O-33]}function k(O,e){let a=S(O);return a!="u"&&!(e&&a=="f")}function G(O,e,a,r){if(S(O.next)=="s"||(O.next==63||O.next==58||O.next==45)&&k(O.peek(1),a))O.advance();else return!1;let P=O.pos;for(;;){let Q=O.next,n=0,R=r+1;for(;z(Q);){if(o(Q)){if(e)return!1;R=0}else R++;Q=O.peek(++n)}if(!(Q>=0&&(Q==58?k(O.peek(n+1),a):Q==35?O.peek(n-1)!=32:k(Q,a)))||!a&&R<=r||R==0&&!a&&(c(O,45,n)||c(O,46,n)))break;if(e&&S(Q)=="f")return!1;for(let h=n;h>=0;h--)O.advance();if(e&&O.pos>P+1024)return!1}return!0}const RO=new i((O,e)=>{if(O.next==33)C(O),O.acceptToken(eO);else if(O.next==38||O.next==42){let a=O.next==38?J:OO;m(O),O.acceptToken(a)}else O.next==39||O.next==34?(T(O,!1),O.acceptToken(K)):G(O,!1,e.context.type==d,e.context.depth)&&O.acceptToken(F)}),SO=new i((O,e)=>{let a=e.context.type==g?e.context.depth:-1,r=O.pos;O:for(;;){let P=0,Q=O.next;for(;Q==32;)Q=O.peek(++P);if(!P&&(c(O,45,P)||c(O,46,P))||!o(Q)&&(a<0&&(a=Math.max(e.context.depth+1,P)),P<a))break;for(;;){if(O.next<0)break O;let n=o(O.next);if(O.advance(),n)continue O;r=O.pos}}O.acceptTokenTo(Y,r)}),iO=Z({DirectiveName:t.keyword,DirectiveContent:t.attributeValue,"DirectiveEnd DocEnd":t.meta,QuotedLiteral:t.string,BlockLiteralHeader:t.special(t.string),BlockLiteralContent:t.content,Literal:t.content,"Key/Literal Key/QuotedLiteral":t.definition(t.propertyName),"Anchor Alias":t.labelName,Tag:t.typeName,Comment:t.lineComment,": , -":t.separator,"?":t.punctuation,"[ ]":t.squareBracket,"{ }":t.brace}),kO=V.deserialize({version:14,states:"5lQ!ZQgOOO#PQfO'#CpO#uQfO'#DOOOQR'#Dv'#DvO$qQgO'#DRO%gQdO'#DUO%nQgO'#DUO&ROaO'#D[OOQR'#Du'#DuO&{QgO'#D^O'rQgO'#D`OOQR'#Dt'#DtO(iOqO'#DbOOQP'#Dj'#DjO(zQaO'#CmO)YQgO'#CmOOQP'#Cm'#CmQ)jQaOOQ)uQgOOQ]QgOOO*PQdO'#CrO*nQdO'#CtOOQO'#Dw'#DwO+]Q`O'#CxO+hQdO'#CwO+rQ`O'#CwOOQO'#Cv'#CvO+wQdO'#CvOOQO'#Cq'#CqO,UQ`O,59[O,^QfO,59[OOQR,59[,59[OOQO'#Cx'#CxO,eQ`O'#DPO,pQdO'#DPOOQO'#Dx'#DxO,zQdO'#DxO-XQ`O,59jO-aQfO,59jOOQR,59j,59jOOQR'#DS'#DSO-hQcO,59mO-sQgO'#DVO.TQ`O'#DVO.YQcO,59pOOQR'#DX'#DXO#|QfO'#DWO.hQcO'#DWOOQR,59v,59vO.yOWO,59vO/OOaO,59vO/WOaO,59vO/cQgO'#D_OOQR,59x,59xO0VQgO'#DaOOQR,59z,59zOOQP,59|,59|O0yOaO,59|O1ROaO,59|O1aOqO,59|OOQP-E7h-E7hO1oQgO,59XOOQP,59X,59XO2PQaO'#DeO2_QgO'#DeO2oQgO'#DkOOQP'#Dk'#DkQ)jQaOOO3PQdO'#CsOOQO,59^,59^O3kQdO'#CuOOQO,59`,59`OOQO,59c,59cO4VQdO,59cO4aQdO'#CzO4kQ`O'#CzOOQO,59b,59bOOQU,5:Q,5:QOOQR1G.v1G.vO4pQ`O1G.vOOQU-E7d-E7dO4xQdO,59kOOQO,59k,59kO5SQdO'#DQO5^Q`O'#DQOOQO,5:d,5:dOOQU,5:R,5:ROOQR1G/U1G/UO5cQ`O1G/UOOQU-E7e-E7eO5kQgO'#DhO5xQcO1G/XOOQR1G/X1G/XOOQR,59q,59qO6TQgO,59qO6eQdO'#DiO6lQgO'#DiO7PQcO1G/[OOQR1G/[1G/[OOQR,59r,59rO#|QfO,59rOOQR1G/b1G/bO7_OWO1G/bO7dOaO1G/bOOQR,59y,59yOOQR,59{,59{OOQP1G/h1G/hO7lOaO1G/hO7tOaO1G/hO8POaO1G/hOOQP1G.s1G.sO8_QgO,5:POOQP,5:P,5:POOQP,5:V,5:VOOQP-E7i-E7iOOQO,59_,59_OOQO,59a,59aOOQO1G.}1G.}OOQO,59f,59fO8oQdO,59fOOQR7+$b7+$bP,XQ`O'#DfOOQO1G/V1G/VOOQO,59l,59lO8yQdO,59lOOQR7+$p7+$pP9TQ`O'#DgOOQR'#DT'#DTOOQR,5:S,5:SOOQR-E7f-E7fOOQR7+$s7+$sOOQR1G/]1G/]O9YQgO'#DYO9jQ`O'#DYOOQR,5:T,5:TO#|QfO'#DZO9oQcO'#DZOOQR-E7g-E7gOOQR7+$v7+$vOOQR1G/^1G/^OOQR7+$|7+$|O:QOWO7+$|OOQP7+%S7+%SO:VOaO7+%SO:_OaO7+%SOOQP1G/k1G/kOOQO1G/Q1G/QOOQO1G/W1G/WOOQR,59t,59tO:jQgO,59tOOQR,59u,59uO#|QfO,59uOOQR<<Hh<<HhOOQP<<Hn<<HnO:zOaO<<HnOOQR1G/`1G/`OOQR1G/a1G/aOOQPAN>YAN>Y",stateData:";S~O!fOS!gOS^OS~OP_OQbORSOTUOWROXROYYOZZO[XOcPOqQO!PVO!V[O!cTO~O`cO~P]OVkOWROXROYeOZfO[dOcPOmhOqQO~OboO~P!bOVtOWROXROYeOZfO[dOcPOmrOqQO~OpwO~P#WORSOTUOWROXROYYOZZO[XOcPOqQO!PVO!cTO~OSvP!avP!bvP~P#|OWROXROYeOZfO[dOcPOqQO~OmzO~P%OOm!OOUzP!azP!bzP!dzP~P#|O^!SO!b!QO!f!TO!g!RO~ORSOTUOWROXROcPOqQO!PVO!cTO~OY!UOP!QXQ!QX!V!QX!`!QXS!QX!a!QX!b!QXU!QXm!QX!d!QX~P&aO[!WOP!SXQ!SX!V!SX!`!SXS!SX!a!SX!b!SXU!SXm!SX!d!SX~P&aO^!ZO!W![O!b!YO!f!]O!g!YO~OP!_O!V[OQaX!`aX~OPaXQaX!VaX!`aX~P#|OP!bOQ!cO!V[O~OP_O!V[O~P#|OWROXROY!fOcPOqQObfXmfXofXpfX~OWROXRO[!hOcPOqQObhXmhXohXphX~ObeXmlXoeX~ObkXokX~P%OOm!kO~Om!lObnPonP~P%OOb!pOo!oO~Ob!pO~P!bOm!sOosXpsX~OosXpsX~P%OOm!uOotPptP~P%OOo!xOp!yO~Op!yO~P#WOS!|O!a#OO!b#OO~OUyX!ayX!byX!dyX~P#|Om#QO~OU#SO!a#UO!b#UO!d#RO~Om#WOUzX!azX!bzX!dzX~O]#XO~O!b#XO!g#YO~O^#ZO!b#XO!g#YO~OP!RXQ!RX!V!RX!`!RXS!RX!a!RX!b!RXU!RXm!RX!d!RX~P&aOP!TXQ!TX!V!TX!`!TXS!TX!a!TX!b!TXU!TXm!TX!d!TX~P&aO!b#^O!g#^O~O^#_O!b#^O!f#`O!g#^O~O^#_O!W#aO!b#^O!g#^O~OPaaQaa!Vaa!`aa~P#|OP#cO!V[OQ!XX!`!XX~OP!XXQ!XX!V!XX!`!XX~P#|OP_O!V[OQ!_X!`!_X~P#|OWROXROcPOqQObgXmgXogXpgX~OWROXROcPOqQObiXmiXoiXpiX~Obkaoka~P%OObnXonX~P%OOm#kO~Ob#lOo!oO~Oosapsa~P%OOotXptX~P%OOm#pO~Oo!xOp#qO~OSwP!awP!bwP~P#|OS!|O!a#vO!b#vO~OUya!aya!bya!dya~P#|Om#xO~P%OOm#{OU}P!a}P!b}P!d}P~P#|OU#SO!a$OO!b$OO!d#RO~O]$QO~O!b$QO!g$RO~O!b$SO!g$SO~O^$TO!b$SO!g$SO~O^$TO!b$SO!f$UO!g$SO~OP!XaQ!Xa!V!Xa!`!Xa~P#|Obnaona~P%OOotapta~P%OOo!xO~OU|X!a|X!b|X!d|X~P#|Om$ZO~Om$]OU}X!a}X!b}X!d}X~O]$^O~O!b$_O!g$_O~O^$`O!b$_O!g$_O~OU|a!a|a!b|a!d|a~P#|O!b$cO!g$cO~O",goto:",]!mPPPPPPPPPPPPPPPPP!nPP!v#v#|$`#|$c$f$j$nP%VPPP!v%Y%^%a%{&O%a&R&U&X&_&b%aP&e&{&e'O'RPP']'a'g'm's'y(XPPPPPPPP(_)e*X+c,VUaObcR#e!c!{ROPQSTUXY_bcdehknrtvz!O!U!W!_!b!c!f!h!k!l!s!u!|#Q#R#S#W#c#k#p#x#{$Z$]QmPR!qnqfPQThknrtv!k!l!s!u#R#k#pR!gdR!ieTlPnTjPnSiPnSqQvQ{TQ!mkQ!trQ!vtR#y#RR!nkTsQvR!wt!RWOSUXY_bcz!O!U!W!_!b!c!|#Q#S#W#c#x#{$Z$]RySR#t!|R|TR|UQ!PUR#|#SR#z#RR#z#SyZOSU_bcz!O!_!b!c!|#Q#S#W#c#x#{$Z$]R!VXR!XYa]O^abc!a!c!eT!da!eQnPR!rnQvQR!{vQ!}yR#u!}Q#T|R#}#TW^Obc!cS!^^!aT!aa!eQ!eaR#f!eW`Obc!cQxSS}U#SQ!`_Q#PzQ#V!OQ#b!_Q#d!bQ#s!|Q#w#QQ$P#WQ$V#cQ$Y#xQ$[#{Q$a$ZR$b$]xZOSU_bcz!O!_!b!c!|#Q#S#W#c#x#{$Z$]Q!VXQ!XYQ#[!UR#]!W!QWOSUXY_bcz!O!U!W!_!b!c!|#Q#S#W#c#x#{$Z$]pfPQThknrtv!k!l!s!u#R#k#pQ!gdQ!ieQ#g!fR#h!hSgPn^pQTkrtv#RQ!jhQ#i!kQ#j!lQ#n!sQ#o!uQ$W#kR$X#pQuQR!zv",nodeNames:"⚠ DirectiveEnd DocEnd - - ? ? ? Literal QuotedLiteral Anchor Alias Tag BlockLiteralContent Comment Stream BOM Document ] [ FlowSequence Item Tagged Anchored Anchored Tagged FlowMapping Pair Key : Pair , } { FlowMapping Pair Pair BlockSequence Item Item BlockMapping Pair Pair Key Pair Pair BlockLiteral BlockLiteralHeader Tagged Anchored Anchored Tagged Directive DirectiveName DirectiveContent Document",maxTerm:74,context:sO,nodeProps:[["isolate",-3,8,9,14,""],["openedBy",18,"[",32,"{"],["closedBy",19,"]",33,"}"]],propSources:[iO],skippedNodes:[0],repeatNodeCount:6,tokenData:"-Y~RnOX#PXY$QYZ$]Z]#P]^$]^p#Ppq$Qqs#Pst$btu#Puv$yv|#P|}&e}![#P![!]'O!]!`#P!`!a'i!a!}#P!}#O*g#O#P#P#P#Q+Q#Q#o#P#o#p+k#p#q'i#q#r,U#r;'S#P;'S;=`#z<%l?HT#P?HT?HU,o?HUO#PQ#UU!WQOY#PZp#Ppq#hq;'S#P;'S;=`#z<%lO#PQ#kTOY#PZs#Pt;'S#P;'S;=`#z<%lO#PQ#}P;=`<%l#P~$VQ!f~XY$Qpq$Q~$bO!g~~$gS^~OY$bZ;'S$b;'S;=`$s<%lO$b~$vP;=`<%l$bR%OX!WQOX%kXY#PZ]%k]^#P^p%kpq#hq;'S%k;'S;=`&_<%lO%kR%rX!WQ!VPOX%kXY#PZ]%k]^#P^p%kpq#hq;'S%k;'S;=`&_<%lO%kR&bP;=`<%l%kR&lUoP!WQOY#PZp#Ppq#hq;'S#P;'S;=`#z<%lO#PR'VUmP!WQOY#PZp#Ppq#hq;'S#P;'S;=`#z<%lO#PR'p[!PP!WQOY#PZp#Ppq#hq{#P{|(f|}#P}!O(f!O!R#P!R![)p![;'S#P;'S;=`#z<%lO#PR(mW!PP!WQOY#PZp#Ppq#hq!R#P!R![)V![;'S#P;'S;=`#z<%lO#PR)^U!PP!WQOY#PZp#Ppq#hq;'S#P;'S;=`#z<%lO#PR)wY!PP!WQOY#PZp#Ppq#hq{#P{|)V|}#P}!O)V!O;'S#P;'S;=`#z<%lO#PR*nUcP!WQOY#PZp#Ppq#hq;'S#P;'S;=`#z<%lO#PR+XUbP!WQOY#PZp#Ppq#hq;'S#P;'S;=`#z<%lO#PR+rUqP!WQOY#PZp#Ppq#hq;'S#P;'S;=`#z<%lO#PR,]UpP!WQOY#PZp#Ppq#hq;'S#P;'S;=`#z<%lO#PR,vU`P!WQOY#PZp#Ppq#hq;'S#P;'S;=`#z<%lO#P",tokenizers:[lO,fO,RO,SO,0,1],topRules:{Stream:[0,15]},tokenPrec:0}),bO=L.define({name:"yaml",parser:kO.configure({props:[B.add({Stream:O=>{for(let e=O.node.resolve(O.pos,-1);e&&e.to>=O.pos;e=e.parent){if(e.name=="BlockLiteralContent"&&e.from<e.to)return O.baseIndentFor(e);if(e.name=="BlockLiteral")return O.baseIndentFor(e)+O.unit;if(e.name=="BlockSequence"||e.name=="BlockMapping")return O.column(e.from,1);if(e.name=="QuotedLiteral")return null;if(e.name=="Literal"){let a=O.column(e.from,1);if(a==O.lineIndent(e.from,1))return a;if(e.to>O.pos)return null}}return null},FlowMapping:v({closing:"}"}),FlowSequence:v({closing:"]"})}),E.add({"FlowMapping FlowSequence":M,"Item Pair BlockLiteral":(O,e)=>({from:e.doc.lineAt(O.from).to,to:O.to})})]}),languageData:{commentTokens:{line:"#"},indentOnInput:/^\s*[\]\}]$/}});function hO(){return new w(bO)}export{hO as yaml,bO as yamlLanguage};
|