@bendyline/docblocks-cli 1.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +60 -76
  2. package/dist/commands/build.d.ts +11 -0
  3. package/dist/commands/build.d.ts.map +1 -1
  4. package/dist/commands/build.js +61 -45
  5. package/dist/commands/build.js.map +1 -1
  6. package/dist/commands/convert.d.ts +12 -2
  7. package/dist/commands/convert.d.ts.map +1 -1
  8. package/dist/commands/convert.js +272 -101
  9. package/dist/commands/convert.js.map +1 -1
  10. package/dist/commands/mcp.d.ts +1 -1
  11. package/dist/commands/mcp.d.ts.map +1 -1
  12. package/dist/commands/mcp.js +90 -4
  13. package/dist/commands/mcp.js.map +1 -1
  14. package/dist/commands/parse.js +1 -1
  15. package/dist/commands/parse.js.map +1 -1
  16. package/dist/commands/serve.d.ts +33 -0
  17. package/dist/commands/serve.d.ts.map +1 -1
  18. package/dist/commands/serve.js +340 -3
  19. package/dist/commands/serve.js.map +1 -1
  20. package/dist/commands/themes.js +1 -1
  21. package/dist/commands/themes.js.map +1 -1
  22. package/dist/commands/transforms.js +1 -1
  23. package/dist/commands/transforms.js.map +1 -1
  24. package/dist/commands/video.d.ts +8 -1
  25. package/dist/commands/video.d.ts.map +1 -1
  26. package/dist/commands/video.js +50 -30
  27. package/dist/commands/video.js.map +1 -1
  28. package/dist/contained-file.d.ts +8 -0
  29. package/dist/contained-file.d.ts.map +1 -0
  30. package/dist/contained-file.js +77 -0
  31. package/dist/contained-file.js.map +1 -0
  32. package/dist/index.js +3 -2
  33. package/dist/index.js.map +1 -1
  34. package/dist/mcp/agentic-tools.d.ts +16 -0
  35. package/dist/mcp/agentic-tools.d.ts.map +1 -0
  36. package/dist/mcp/agentic-tools.js +967 -0
  37. package/dist/mcp/agentic-tools.js.map +1 -0
  38. package/dist/mcp/artifact-store.d.ts +98 -0
  39. package/dist/mcp/artifact-store.d.ts.map +1 -0
  40. package/dist/mcp/artifact-store.js +542 -0
  41. package/dist/mcp/artifact-store.js.map +1 -0
  42. package/dist/mcp/authority.d.ts +53 -0
  43. package/dist/mcp/authority.d.ts.map +1 -0
  44. package/dist/mcp/authority.js +433 -0
  45. package/dist/mcp/authority.js.map +1 -0
  46. package/dist/mcp/conversion-service.d.ts +111 -0
  47. package/dist/mcp/conversion-service.d.ts.map +1 -0
  48. package/dist/mcp/conversion-service.js +512 -0
  49. package/dist/mcp/conversion-service.js.map +1 -0
  50. package/dist/mcp/discovery-tools.d.ts +4 -0
  51. package/dist/mcp/discovery-tools.d.ts.map +1 -0
  52. package/dist/mcp/discovery-tools.js +114 -0
  53. package/dist/mcp/discovery-tools.js.map +1 -0
  54. package/dist/mcp/document-service.d.ts +47 -0
  55. package/dist/mcp/document-service.d.ts.map +1 -0
  56. package/dist/mcp/document-service.js +424 -0
  57. package/dist/mcp/document-service.js.map +1 -0
  58. package/dist/mcp/error-result.d.ts +17 -0
  59. package/dist/mcp/error-result.d.ts.map +1 -0
  60. package/dist/mcp/error-result.js +82 -0
  61. package/dist/mcp/error-result.js.map +1 -0
  62. package/dist/mcp/intelligence.d.ts +15 -0
  63. package/dist/mcp/intelligence.d.ts.map +1 -0
  64. package/dist/mcp/intelligence.js +855 -0
  65. package/dist/mcp/intelligence.js.map +1 -0
  66. package/dist/mcp/output-bounds.d.ts +13 -0
  67. package/dist/mcp/output-bounds.d.ts.map +1 -0
  68. package/dist/mcp/output-bounds.js +61 -0
  69. package/dist/mcp/output-bounds.js.map +1 -0
  70. package/dist/mcp/preview-service.d.ts +54 -0
  71. package/dist/mcp/preview-service.d.ts.map +1 -0
  72. package/dist/mcp/preview-service.js +454 -0
  73. package/dist/mcp/preview-service.js.map +1 -0
  74. package/dist/mcp/progress.d.ts +18 -0
  75. package/dist/mcp/progress.d.ts.map +1 -0
  76. package/dist/mcp/progress.js +28 -0
  77. package/dist/mcp/progress.js.map +1 -0
  78. package/dist/mcp/prompts.d.ts +4 -0
  79. package/dist/mcp/prompts.d.ts.map +1 -0
  80. package/dist/mcp/prompts.js +124 -0
  81. package/dist/mcp/prompts.js.map +1 -0
  82. package/dist/mcp/rendered-conversion.d.ts +34 -0
  83. package/dist/mcp/rendered-conversion.d.ts.map +1 -0
  84. package/dist/mcp/rendered-conversion.js +364 -0
  85. package/dist/mcp/rendered-conversion.js.map +1 -0
  86. package/dist/mcp/server.d.ts +67 -10
  87. package/dist/mcp/server.d.ts.map +1 -1
  88. package/dist/mcp/server.js +228 -496
  89. package/dist/mcp/server.js.map +1 -1
  90. package/dist/preview-policy.d.ts +8 -0
  91. package/dist/preview-policy.d.ts.map +1 -0
  92. package/dist/preview-policy.js +85 -0
  93. package/dist/preview-policy.js.map +1 -0
  94. package/dist/render-html.d.ts +17 -0
  95. package/dist/render-html.d.ts.map +1 -0
  96. package/dist/render-html.js +85 -0
  97. package/dist/render-html.js.map +1 -0
  98. package/dist/version.d.ts +6 -0
  99. package/dist/version.d.ts.map +1 -0
  100. package/dist/version.js +104 -0
  101. package/dist/version.js.map +1 -0
  102. package/package.json +11 -11
  103. package/dist/converters/docx-to-md.d.ts +0 -10
  104. package/dist/converters/docx-to-md.d.ts.map +0 -1
  105. package/dist/converters/docx-to-md.js +0 -17
  106. package/dist/converters/docx-to-md.js.map +0 -1
  107. package/dist/converters/pdf-to-md.d.ts +0 -14
  108. package/dist/converters/pdf-to-md.d.ts.map +0 -1
  109. package/dist/converters/pdf-to-md.js +0 -51
  110. package/dist/converters/pdf-to-md.js.map +0 -1
  111. package/dist/converters/pptx-to-md.d.ts +0 -13
  112. package/dist/converters/pptx-to-md.d.ts.map +0 -1
  113. package/dist/converters/pptx-to-md.js +0 -67
  114. package/dist/converters/pptx-to-md.js.map +0 -1
@@ -1,3 +1,36 @@
1
1
  import { Command } from 'commander';
2
+ import { type Server } from 'node:http';
3
+ export interface ServeOptions {
4
+ port: number;
5
+ dir: string;
6
+ theme?: string;
7
+ host?: string;
8
+ allowNetwork?: boolean;
9
+ maxFileBytes?: number;
10
+ maxConcurrentReads?: number;
11
+ }
12
+ export interface PreviewServer {
13
+ server: Server;
14
+ root: string;
15
+ host: string;
16
+ url: string;
17
+ }
18
+ export declare function startPreviewServer(opts: ServeOptions): Promise<PreviewServer>;
2
19
  export declare const serveCommand: Command;
20
+ type ServeTarget = {
21
+ kind: 'file';
22
+ filePath: string;
23
+ markdown: boolean;
24
+ } | {
25
+ kind: 'missing';
26
+ } | {
27
+ kind: 'forbidden';
28
+ } | {
29
+ kind: 'too-large';
30
+ } | {
31
+ kind: 'bad-request';
32
+ };
33
+ export declare function resolveServeTarget(root: string, requestUrl: string, maxFileBytes?: number): Promise<ServeTarget>;
34
+ export declare function isAllowedPreviewRequestAuthority(header: string | undefined, configuredHost: string, configuredPort: number): boolean;
35
+ export {};
3
36
  //# sourceMappingURL=serve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,eAAO,MAAM,YAAY,SASrB,CAAC"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAsC,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAC;AAQjG,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAKD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CA8CnF;AAED,eAAO,MAAM,YAAY,SA8BtB,CAAC;AAuDJ,KAAK,WAAW,GACZ;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAE5B,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,YAAY,SAAyB,GACpC,OAAO,CAAC,WAAW,CAAC,CAkCtB;AAqHD,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,GACrB,OAAO,CAOT"}
@@ -1,10 +1,347 @@
1
1
  import { Command } from 'commander';
2
+ import { createServer } from 'node:http';
3
+ import { realpath, stat } from 'node:fs/promises';
4
+ import { isIP } from 'node:net';
5
+ import path from 'node:path';
6
+ import { renderMarkdownHtml } from '../render-html.js';
7
+ import { readContainedFile } from '../contained-file.js';
8
+ import { isAllowedPreviewPath } from '../preview-policy.js';
9
+ const DEFAULT_HOST = '127.0.0.1';
10
+ const DEFAULT_MAX_FILE_BYTES = 50 * 1024 * 1024;
11
+ const DEFAULT_MAX_CONCURRENT_READS = 16;
12
+ export async function startPreviewServer(opts) {
13
+ const requestedRoot = path.resolve(opts.dir);
14
+ const root = await realpath(requestedRoot).catch(() => requestedRoot);
15
+ const rootStat = await stat(root).catch(() => null);
16
+ if (!rootStat?.isDirectory()) {
17
+ throw new Error(`Directory not found: ${root}`);
18
+ }
19
+ const host = validateHost(opts.host ?? DEFAULT_HOST, opts.allowNetwork === true);
20
+ const maxFileBytes = positiveLimit(opts.maxFileBytes, DEFAULT_MAX_FILE_BYTES, 'file size');
21
+ const maxConcurrentReads = positiveLimit(opts.maxConcurrentReads, DEFAULT_MAX_CONCURRENT_READS, 'concurrent request');
22
+ let activeRequests = 0;
23
+ const server = createServer((req, res) => {
24
+ const address = server.address();
25
+ const port = typeof address === 'object' && address ? address.port : opts.port;
26
+ if (!isAllowedPreviewRequestAuthority(req.headers.host, host, port)) {
27
+ sendText(res, 421, 'Misdirected request', req.method === 'HEAD');
28
+ return;
29
+ }
30
+ if (activeRequests >= maxConcurrentReads) {
31
+ sendText(res, 503, 'Server busy', req.method === 'HEAD');
32
+ return;
33
+ }
34
+ activeRequests += 1;
35
+ handlePreviewRequest(req, res, root, opts.theme, maxFileBytes)
36
+ .catch(() => sendText(res, 500, 'Internal server error', req.method === 'HEAD'))
37
+ .finally(() => {
38
+ activeRequests -= 1;
39
+ });
40
+ });
41
+ await new Promise((resolve, reject) => {
42
+ server.once('error', reject);
43
+ server.listen(opts.port, host, () => {
44
+ server.off('error', reject);
45
+ resolve();
46
+ });
47
+ });
48
+ const address = server.address();
49
+ const port = typeof address === 'object' && address ? address.port : opts.port;
50
+ return { server, root, host, url: `http://${urlHost(host)}:${port}/` };
51
+ }
2
52
  export const serveCommand = new Command('serve')
3
53
  .description('Start a local dev server for previewing markdown files')
4
54
  .option('-p, --port <port>', 'port to listen on', '3000')
5
55
  .option('-d, --dir <dir>', 'directory to serve', '.')
6
- .action((opts) => {
7
- console.error(`serve command is not yet implemented. Would serve ${opts.dir} on port ${opts.port}.`);
8
- console.error('This will be implemented in a future release.');
56
+ .option('-t, --theme <id>', 'Squisq theme ID to apply')
57
+ .option('--host <host>', 'interface to bind (loopback by default)', DEFAULT_HOST)
58
+ .option('--allow-network', 'allow a non-loopback --host')
59
+ .action(async (opts) => {
60
+ try {
61
+ const server = await startPreviewServer({
62
+ port: parsePort(opts.port),
63
+ dir: opts.dir,
64
+ theme: opts.theme,
65
+ host: opts.host,
66
+ allowNetwork: opts.allowNetwork,
67
+ });
68
+ console.error(`Serving ${server.root} at ${server.url}`);
69
+ }
70
+ catch (err) {
71
+ const message = err instanceof Error ? err.message : String(err);
72
+ console.error(`Error: ${message}`);
73
+ process.exitCode = 1;
74
+ }
9
75
  });
76
+ async function handlePreviewRequest(req, res, root, themeId, maxFileBytes) {
77
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
78
+ sendText(res, 405, 'Method not allowed');
79
+ return;
80
+ }
81
+ const headOnly = req.method === 'HEAD';
82
+ const target = await resolveServeTarget(root, req.url ?? '/', maxFileBytes);
83
+ if (target.kind === 'bad-request') {
84
+ sendText(res, 400, 'Bad request', headOnly);
85
+ return;
86
+ }
87
+ if (target.kind === 'forbidden') {
88
+ sendText(res, 403, 'Forbidden', headOnly);
89
+ return;
90
+ }
91
+ if (target.kind === 'missing') {
92
+ sendText(res, 404, 'Not found', headOnly);
93
+ return;
94
+ }
95
+ if (target.kind === 'too-large') {
96
+ sendText(res, 413, 'File too large', headOnly);
97
+ return;
98
+ }
99
+ if (target.markdown) {
100
+ const markdown = (await readContainedFile(root, target.filePath, maxFileBytes)).toString('utf8');
101
+ const html = await renderMarkdownHtml(markdown, {
102
+ title: path.basename(target.filePath).replace(/\.(md|markdown)$/i, ''),
103
+ sourcePath: target.filePath,
104
+ assetRoot: root,
105
+ themeId,
106
+ mode: 'static',
107
+ allowReferencedAsset: ({ assetRoot, requestedPath, physicalPath }) => isAllowedPreviewPath(assetRoot, requestedPath, physicalPath, 'embedded-image'),
108
+ });
109
+ sendBody(res, 200, html, 'text/html; charset=utf-8', headOnly);
110
+ return;
111
+ }
112
+ const body = await readContainedFile(root, target.filePath, maxFileBytes);
113
+ sendBody(res, 200, body, contentTypeFor(target.filePath), headOnly);
114
+ }
115
+ export async function resolveServeTarget(root, requestUrl, maxFileBytes = DEFAULT_MAX_FILE_BYTES) {
116
+ if (requestUrl.length > 8192 || requestUrl.includes('\0'))
117
+ return { kind: 'bad-request' };
118
+ const physicalRoot = await realpath(path.resolve(root)).catch(() => null);
119
+ if (!physicalRoot)
120
+ return { kind: 'missing' };
121
+ const decodedPath = decodeRequestPath(requestUrl);
122
+ if (!decodedPath)
123
+ return { kind: 'bad-request' };
124
+ const requestedPath = decodedPath.replace(/^\/+/, '');
125
+ const candidate = path.resolve(physicalRoot, requestedPath);
126
+ if (!isAllowedPreviewPath(physicalRoot, candidate, candidate, 'directory')) {
127
+ return { kind: 'forbidden' };
128
+ }
129
+ const direct = await inspectTarget(physicalRoot, candidate, maxFileBytes);
130
+ if (direct.kind === 'file')
131
+ return direct;
132
+ if (direct.kind === 'forbidden' || direct.kind === 'too-large')
133
+ return direct;
134
+ if (direct.kind === 'directory') {
135
+ const dirIndex = await directoryIndexTarget(physicalRoot, direct.filePath, maxFileBytes);
136
+ if (dirIndex.kind !== 'missing')
137
+ return dirIndex;
138
+ }
139
+ if (/\.html?$/i.test(candidate)) {
140
+ const markdownTarget = await inspectTarget(physicalRoot, candidate.replace(/\.html?$/i, '.md'), maxFileBytes);
141
+ if (markdownTarget.kind !== 'directory' && markdownTarget.kind !== 'missing') {
142
+ return markdownTarget;
143
+ }
144
+ }
145
+ return { kind: 'missing' };
146
+ }
147
+ function decodeRequestPath(requestUrl) {
148
+ const pathOnly = requestUrl.split(/[?#]/, 1)[0] || '/';
149
+ try {
150
+ return decodeURIComponent(pathOnly);
151
+ }
152
+ catch {
153
+ return '';
154
+ }
155
+ }
156
+ async function inspectTarget(root, candidate, maxFileBytes) {
157
+ let physical;
158
+ try {
159
+ physical = await realpath(candidate);
160
+ }
161
+ catch {
162
+ return { kind: 'missing' };
163
+ }
164
+ const info = await stat(physical).catch(() => null);
165
+ if (!info)
166
+ return { kind: 'missing' };
167
+ if (info.isDirectory()) {
168
+ return isAllowedPreviewPath(root, candidate, physical, 'directory')
169
+ ? { kind: 'directory', filePath: physical }
170
+ : { kind: 'forbidden' };
171
+ }
172
+ if (!info.isFile())
173
+ return { kind: 'forbidden' };
174
+ if (!isAllowedPreviewPath(root, candidate, physical, 'document-or-asset')) {
175
+ return { kind: 'forbidden' };
176
+ }
177
+ if (info.size > maxFileBytes)
178
+ return { kind: 'too-large' };
179
+ return { kind: 'file', filePath: physical, markdown: /\.(md|markdown)$/i.test(physical) };
180
+ }
181
+ async function directoryIndexTarget(root, dirPath, maxFileBytes) {
182
+ for (const fileName of ['index.html', 'index.htm', 'index.md', 'index.markdown']) {
183
+ const target = await inspectTarget(root, path.join(dirPath, fileName), maxFileBytes);
184
+ if (target.kind === 'file' || target.kind === 'forbidden' || target.kind === 'too-large') {
185
+ return target;
186
+ }
187
+ }
188
+ return { kind: 'missing' };
189
+ }
190
+ function parsePort(value) {
191
+ const port = Number(value);
192
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
193
+ throw new Error(`Invalid port: ${value}`);
194
+ }
195
+ return port;
196
+ }
197
+ function sendText(res, status, message, headOnly = false) {
198
+ sendBody(res, status, `${message}\n`, 'text/plain; charset=utf-8', headOnly);
199
+ }
200
+ function sendBody(res, status, body, contentType, headOnly) {
201
+ res.statusCode = status;
202
+ res.setHeader('content-type', contentType);
203
+ res.setHeader('x-content-type-options', 'nosniff');
204
+ res.setHeader('referrer-policy', 'no-referrer');
205
+ res.setHeader('cache-control', 'no-store');
206
+ if (headOnly) {
207
+ res.end();
208
+ return;
209
+ }
210
+ res.end(body);
211
+ }
212
+ function validateHost(value, allowNetwork) {
213
+ const rawHost = value.trim().toLowerCase();
214
+ const host = rawHost.startsWith('[') && rawHost.endsWith(']') ? rawHost.slice(1, -1) : rawHost;
215
+ if (!host ||
216
+ host.length > 255 ||
217
+ /[\s/\\\0]/.test(host) ||
218
+ (isIP(host) === 0 && !isValidHostname(host))) {
219
+ throw new Error('Invalid host');
220
+ }
221
+ if (!allowNetwork && !isLoopbackHost(host)) {
222
+ throw new Error('Non-loopback hosts require --allow-network');
223
+ }
224
+ return host;
225
+ }
226
+ function isLoopbackHost(host) {
227
+ if (host === 'localhost' || host === '::1')
228
+ return true;
229
+ const octets = host.split('.');
230
+ return (octets.length === 4 &&
231
+ octets[0] === '127' &&
232
+ octets.every((octet) => /^\d{1,3}$/.test(octet) && Number(octet) <= 255));
233
+ }
234
+ export function isAllowedPreviewRequestAuthority(header, configuredHost, configuredPort) {
235
+ const authority = parseRequestAuthority(header);
236
+ if (!authority || authority.port !== configuredPort)
237
+ return false;
238
+ if (configuredHost === '0.0.0.0')
239
+ return isIP(authority.host) === 4;
240
+ if (configuredHost === '::')
241
+ return isIP(authority.host) === 6;
242
+ return authority.host === configuredHost;
243
+ }
244
+ function parseRequestAuthority(header) {
245
+ if (!header || header.length > 512 || /[\s,\\/\0]/.test(header))
246
+ return null;
247
+ let host;
248
+ let portText = '';
249
+ let hasPortSeparator = false;
250
+ if (header.startsWith('[')) {
251
+ const closingBracket = header.indexOf(']');
252
+ if (closingBracket < 0)
253
+ return null;
254
+ host = header.slice(1, closingBracket).toLowerCase();
255
+ const remainder = header.slice(closingBracket + 1);
256
+ if (remainder && !remainder.startsWith(':'))
257
+ return null;
258
+ hasPortSeparator = remainder.startsWith(':');
259
+ portText = remainder.slice(1);
260
+ if (isIP(host) !== 6)
261
+ return null;
262
+ }
263
+ else {
264
+ const separator = header.lastIndexOf(':');
265
+ if (separator >= 0) {
266
+ if (header.indexOf(':') !== separator)
267
+ return null;
268
+ hasPortSeparator = true;
269
+ host = header.slice(0, separator).toLowerCase();
270
+ portText = header.slice(separator + 1);
271
+ }
272
+ else {
273
+ host = header.toLowerCase();
274
+ }
275
+ if (isIP(host) === 0 && !isValidHostname(host))
276
+ return null;
277
+ }
278
+ if (hasPortSeparator && !/^\d{1,5}$/.test(portText))
279
+ return null;
280
+ const port = portText ? Number(portText) : 80;
281
+ if (!Number.isInteger(port) || port < 1 || port > 65535)
282
+ return null;
283
+ return { host, port };
284
+ }
285
+ function isValidHostname(host) {
286
+ return (host.length <= 253 &&
287
+ !host.endsWith('.') &&
288
+ host.split('.').every((label) => /^(?!-)[a-z0-9-]{1,63}(?<!-)$/.test(label)));
289
+ }
290
+ function urlHost(host) {
291
+ return host.includes(':') && !host.startsWith('[') ? `[${host}]` : host;
292
+ }
293
+ function positiveLimit(value, fallback, label) {
294
+ const selected = value ?? fallback;
295
+ if (!Number.isSafeInteger(selected) || selected < 1)
296
+ throw new Error(`Invalid ${label} limit`);
297
+ return selected;
298
+ }
299
+ function contentTypeFor(filePath) {
300
+ const ext = path.extname(filePath).toLowerCase();
301
+ switch (ext) {
302
+ case '.html':
303
+ case '.htm':
304
+ return 'text/html; charset=utf-8';
305
+ case '.css':
306
+ return 'text/css; charset=utf-8';
307
+ case '.js':
308
+ case '.mjs':
309
+ return 'text/javascript; charset=utf-8';
310
+ case '.json':
311
+ return 'application/json; charset=utf-8';
312
+ case '.svg':
313
+ return 'image/svg+xml';
314
+ case '.png':
315
+ return 'image/png';
316
+ case '.jpg':
317
+ case '.jpeg':
318
+ return 'image/jpeg';
319
+ case '.gif':
320
+ return 'image/gif';
321
+ case '.webp':
322
+ return 'image/webp';
323
+ case '.ico':
324
+ return 'image/x-icon';
325
+ case '.woff':
326
+ return 'font/woff';
327
+ case '.woff2':
328
+ return 'font/woff2';
329
+ case '.ttf':
330
+ return 'font/ttf';
331
+ case '.otf':
332
+ return 'font/otf';
333
+ case '.mp3':
334
+ return 'audio/mpeg';
335
+ case '.wav':
336
+ return 'audio/wav';
337
+ case '.ogg':
338
+ return 'audio/ogg';
339
+ case '.mp4':
340
+ return 'video/mp4';
341
+ case '.webm':
342
+ return 'video/webm';
343
+ default:
344
+ return 'application/octet-stream';
345
+ }
346
+ }
10
347
  //# sourceMappingURL=serve.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,wDAAwD,CAAC;KACrE,MAAM,CAAC,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,CAAC;KACxD,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,EAAE,GAAG,CAAC;KACpD,MAAM,CAAC,CAAC,IAAmC,EAAE,EAAE;IAC9C,OAAO,CAAC,KAAK,CACX,qDAAqD,IAAI,CAAC,GAAG,YAAY,IAAI,CAAC,IAAI,GAAG,CACtF,CAAC;IACF,OAAO,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;AACjE,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@ export const themesCommand = new Command('themes')
9
9
  const themes = getAvailableThemes();
10
10
  console.error('Available themes:\n');
11
11
  for (const theme of themes) {
12
- process.stdout.write(` ${theme}\n`);
12
+ process.stdout.write(`${theme}\n`);
13
13
  }
14
14
  });
15
15
  //# sourceMappingURL=themes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"themes.js","sourceRoot":"","sources":["../../src/commands/themes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,kCAAkC,CAAC;KAC/C,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC;IACzE,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACpC,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACrC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC;IACvC,CAAC;AACH,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"themes.js","sourceRoot":"","sources":["../../src/commands/themes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,kCAAkC,CAAC;KAC/C,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC;IACzE,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACpC,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACrC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;IACrC,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -9,7 +9,7 @@ export const transformsCommand = new Command('transforms')
9
9
  const styles = getTransformStyleIds();
10
10
  console.error('Available transform styles:\n');
11
11
  for (const style of styles) {
12
- process.stdout.write(` ${style}\n`);
12
+ process.stdout.write(`${style}\n`);
13
13
  }
14
14
  });
15
15
  //# sourceMappingURL=transforms.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"transforms.js","sourceRoot":"","sources":["../../src/commands/transforms.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,OAAO,CAAC,YAAY,CAAC;KACvD,WAAW,CAAC,qCAAqC,CAAC;KAClD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC;IAC7E,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;IACtC,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAC/C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC;IACvC,CAAC;AACH,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"transforms.js","sourceRoot":"","sources":["../../src/commands/transforms.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,OAAO,CAAC,YAAY,CAAC;KACvD,WAAW,CAAC,qCAAqC,CAAC;KAClD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC;IAC7E,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;IACtC,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAC/C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;IACrC,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -16,16 +16,23 @@ export interface VideoOptions {
16
16
  captions?: CaptionOption;
17
17
  width?: number;
18
18
  height?: number;
19
+ /** Cancel input preparation, browser capture, or FFmpeg encoding. */
20
+ signal?: AbortSignal;
19
21
  }
20
22
  export interface VideoResult {
21
23
  outputPath: string;
22
24
  duration: number;
23
25
  frameCount: number;
24
26
  }
27
+ type RenderDocToMp4 = typeof import('@bendyline/squisq-cli/api').renderDocToMp4;
28
+ export interface VideoRunDependencies {
29
+ /** Test/programmatic override; production uses the linked Squisq renderer. */
30
+ renderDocToMp4?: RenderDocToMp4;
31
+ }
25
32
  /**
26
33
  * Core video rendering logic — reusable by both CLI and MCP tools.
27
34
  */
28
- export declare function runVideo(inputPath: string, opts: VideoOptions, onProgress?: (phase: string, percent: number) => void): Promise<VideoResult>;
35
+ export declare function runVideo(inputPath: string, opts: VideoOptions, onProgress?: (phase: string, percent: number) => void, dependencies?: VideoRunDependencies): Promise<VideoResult>;
29
36
  export declare const videoCommand: Command;
30
37
  export {};
31
38
  //# sourceMappingURL=video.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"video.d.ts","sourceRoot":"","sources":["../../src/commands/video.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAI9E,QAAA,MAAM,cAAc,wCAAyC,CAAC;AAE9D,KAAK,aAAa,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,YAAY,EAClB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GACpD,OAAO,CAAC,WAAW,CAAC,CAoFtB;AAYD,eAAO,MAAM,YAAY,SAsCrB,CAAC"}
1
+ {"version":3,"file":"video.d.ts","sourceRoot":"","sources":["../../src/commands/video.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAI9E,QAAA,MAAM,cAAc,wCAAyC,CAAC;AAE9D,KAAK,aAAa,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,KAAK,cAAc,GAAG,cAAc,2BAA2B,EAAE,cAAc,CAAC;AAEhF,MAAM,WAAW,oBAAoB;IACnC,8EAA8E;IAC9E,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,YAAY,EAClB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,EACrD,YAAY,GAAE,oBAAyB,GACtC,OAAO,CAAC,WAAW,CAAC,CAsFtB;AAoBD,eAAO,MAAM,YAAY,SAkCrB,CAAC"}
@@ -13,10 +13,11 @@ const VALID_CAPTIONS = ['off', 'standard', 'social'];
13
13
  /**
14
14
  * Core video rendering logic — reusable by both CLI and MCP tools.
15
15
  */
16
- export async function runVideo(inputPath, opts, onProgress) {
16
+ export async function runVideo(inputPath, opts, onProgress, dependencies = {}) {
17
+ throwIfAborted(opts.signal);
17
18
  const resolvedInput = resolve(inputPath);
18
19
  const fps = opts.fps ?? 30;
19
- if (fps < 1 || fps > 120) {
20
+ if (!Number.isFinite(fps) || fps < 1 || fps > 120) {
20
21
  throw new Error('FPS must be a number between 1 and 120');
21
22
  }
22
23
  const quality = opts.quality ?? 'normal';
@@ -40,25 +41,20 @@ export async function runVideo(inputPath, opts, onProgress) {
40
41
  ? resolve(opts.output)
41
42
  : resolve(dirname(resolvedInput), `${baseName}.mp4`);
42
43
  await mkdir(dirname(outputPath), { recursive: true });
44
+ throwIfAborted(opts.signal);
43
45
  console.error(`Reading: ${resolvedInput}`);
44
- const { readInput, renderDocToMp4 } = await import('@bendyline/squisq-cli/api');
45
- const result = await readInput(resolvedInput);
46
- const { container } = result;
47
- // Get or parse Doc
48
- let doc;
49
- if (result.doc) {
50
- console.error('Using pre-built Doc JSON');
51
- doc = result.doc;
52
- }
53
- else if (result.markdownDoc) {
54
- const { markdownToDoc } = await import('@bendyline/squisq/doc');
55
- doc = markdownToDoc(result.markdownDoc);
56
- }
57
- else {
58
- throw new Error('No document found in input');
59
- }
46
+ const squisq = await import('@bendyline/squisq-cli/api');
47
+ throwIfAborted(opts.signal);
48
+ const renderDocToMp4 = dependencies.renderDocToMp4 ?? squisq.renderDocToMp4;
49
+ const { readInput } = squisq;
50
+ const result = await readInput(resolvedInput, { signal: opts.signal });
51
+ throwIfAborted(opts.signal);
52
+ const { container, doc } = result;
53
+ console.error(`Using normalized ${result.sourceFormat} document`);
60
54
  console.error(`Rendering: ${fps} fps, quality: ${quality}, orientation: ${orientation}, captions: ${captions}`);
55
+ let latestProgress = 0;
61
56
  const renderResult = await renderDocToMp4(doc, container, {
57
+ signal: opts.signal,
62
58
  outputPath,
63
59
  fps,
64
60
  quality,
@@ -66,11 +62,20 @@ export async function runVideo(inputPath, opts, onProgress) {
66
62
  width: opts.width,
67
63
  height: opts.height,
68
64
  captionStyle,
69
- onProgress: onProgress ??
70
- ((phase, percent) => {
71
- process.stderr.write(`\r ${phase}: ${percent}% `);
72
- }),
65
+ onProgress: (phase, percent) => {
66
+ throwIfAborted(opts.signal);
67
+ const boundedPercent = Math.max(0, Math.min(100, percent));
68
+ if (boundedPercent < latestProgress)
69
+ return;
70
+ latestProgress = boundedPercent;
71
+ if (onProgress)
72
+ onProgress(phase, boundedPercent);
73
+ else
74
+ process.stderr.write(`\r ${phase}: ${boundedPercent}% `);
75
+ throwIfAborted(opts.signal);
76
+ },
73
77
  });
78
+ throwIfAborted(opts.signal);
74
79
  process.stderr.write('\r' + ' '.repeat(60) + '\r');
75
80
  console.error(` ✓ ${outputPath}`);
76
81
  console.error(` Duration: ${renderResult.duration.toFixed(1)}s, ${renderResult.frameCount} frames`);
@@ -81,15 +86,24 @@ export async function runVideo(inputPath, opts, onProgress) {
81
86
  frameCount: renderResult.frameCount,
82
87
  };
83
88
  }
89
+ function throwIfAborted(signal) {
90
+ if (!signal?.aborted)
91
+ return;
92
+ if (signal.reason !== undefined)
93
+ throw signal.reason;
94
+ const error = new Error('Video rendering was cancelled');
95
+ error.name = 'AbortError';
96
+ throw error;
97
+ }
84
98
  export const videoCommand = new Command('video')
85
99
  .description('Render a squisq document to MP4 video')
86
- .argument('<input>', 'Path to .md file, .zip/.dbk container, or folder')
100
+ .argument('<input>', 'Path to a supported document, .zip/.dbk container, or folder')
87
101
  .argument('[output]', 'Output MP4 path (default: <input>.mp4)')
88
102
  .option('-o, --output <path>', 'Output MP4 path (default: <input>.mp4)')
89
- .option('--fps <number>', 'Frames per second (default: 30)', '30')
90
- .option('--quality <level>', `Encoding quality: ${VALID_QUALITIES.join(', ')} (default: normal)`, 'normal')
91
- .option('--orientation <orient>', `Video orientation: ${VALID_ORIENTATIONS.join(', ')} (default: landscape)`, 'landscape')
92
- .option('--captions <style>', `Caption style: ${VALID_CAPTIONS.join(', ')} (default: off)`, 'off')
103
+ .option('--fps <number>', 'Frames per second', '30')
104
+ .option('--quality <level>', `Encoding quality: ${VALID_QUALITIES.join(', ')}`, 'normal')
105
+ .option('--orientation <orient>', `Video orientation: ${VALID_ORIENTATIONS.join(', ')}`, 'landscape')
106
+ .option('--captions <style>', `Caption style: ${VALID_CAPTIONS.join(', ')}`, 'off')
93
107
  .option('--width <pixels>', 'Override video width')
94
108
  .option('--height <pixels>', 'Override video height')
95
109
  .action(async (inputPath, outputArg, opts) => {
@@ -99,12 +113,12 @@ export const videoCommand = new Command('video')
99
113
  }
100
114
  await runVideo(inputPath, {
101
115
  output: opts.output,
102
- fps: parseInt(opts.fps ?? '30', 10),
116
+ fps: parseNumberOption('--fps', opts.fps ?? '30'),
103
117
  quality: opts.quality,
104
118
  orientation: opts.orientation,
105
119
  captions: opts.captions,
106
- width: opts.width ? parseInt(opts.width, 10) : undefined,
107
- height: opts.height ? parseInt(opts.height, 10) : undefined,
120
+ width: opts.width ? parseNumberOption('--width', opts.width) : undefined,
121
+ height: opts.height ? parseNumberOption('--height', opts.height) : undefined,
108
122
  });
109
123
  }
110
124
  catch (err) {
@@ -113,4 +127,10 @@ export const videoCommand = new Command('video')
113
127
  process.exitCode = 1;
114
128
  }
115
129
  });
130
+ function parseNumberOption(name, value) {
131
+ const parsed = Number(value);
132
+ if (!Number.isFinite(parsed))
133
+ throw new Error(`${name} must be a finite number`);
134
+ return parsed;
135
+ }
116
136
  //# sourceMappingURL=video.js.map