CLI runs on Node 20+: npx storebound (bunx keeps working)
- cli/lib/rt.ts: the runtime layer on node: modules (spawn / spawnSync with Bun's defaults, exit codes and "Executable not found", which with PATHEXT, sleep, serve, Glob, hasher, gunzip, stdin, SQLite, packageRoot, config imports with a type-stripping fallback for Node 20); every Bun.* use in cli/ goes through it - tools/gen-embedded.ts -> cli/lib/embedded.ts: templates/, cli/shots/*.html and Play's Data safety form embedded (replaces Bun text imports; the bundle and the compiled sidecar carry them), checked fresh by bun run ci - package.json: bin dist/storebound.mjs (Node ESM bundle, tools/build-node.ts, prepack), engines node >=20 - bun run ci: node bundle + node smoke (real node: --version, help, check same as Bun, scan, release plan --help, init, MCP handshake + tool call); Forgejo CI sets up Node 20; tests/node-compat.test.ts guards cli/ - install writes npx MCP commands when run with Node; bin/storebound falls back to Node - docs: npx storebound init (Node 20+) or bunx storebound init
This commit is contained in:
parent
614c32bcba
commit
36a8bf9625
73 changed files with 1195 additions and 251 deletions
|
|
@ -21,6 +21,8 @@ import { captionsFor, type CopyFile, copyText, palette, renderDir, scriptFont }
|
|||
import { FONT_TYPES, fontsRoute, resolveShotFonts } from './local-fonts';
|
||||
import { ffprobe } from './report';
|
||||
import { checkVideo, PREVIEW_RULES, VIDEO_TARGETS } from './spec';
|
||||
import { SHOTS_PAGES } from '../lib/embedded';
|
||||
import * as rt from '../lib/rt';
|
||||
|
||||
export const CLIP_RE = /\.(mp4|mov|m4v|webm)$/i;
|
||||
|
||||
|
|
@ -145,13 +147,13 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
if (!dry) mkdirSync(tmp, { recursive: true });
|
||||
|
||||
// caption PNGs come from a local page (fonts from `shots fonts`), like the screenshots
|
||||
let server: ReturnType<typeof Bun.serve> | null = null, browser: { close(): Promise<void>; newContext(o: unknown): Promise<{ newPage(): Promise<unknown> }> } | null = null;
|
||||
let server: rt.Server | null = null, browser: { close(): Promise<void>; newContext(o: unknown): Promise<{ newPage(): Promise<unknown> }> } | null = null;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let page: any = null;
|
||||
if (captions) {
|
||||
const fontsDir = join(root, '.storebound', 'fonts');
|
||||
if (!existsSync(join(fontsDir, 'fonts.css'))) warn('shots', 'no .storebound/fonts/fonts.css: run `storebound shots fonts` first (captions fall back to system fonts)');
|
||||
server = Bun.serve({ port: a.num('port', 8793), hostname: '127.0.0.1', async fetch(req) {
|
||||
server = await rt.serve({ port: a.num('port', 8793), hostname: '127.0.0.1', async fetch(req) {
|
||||
const url = new URL(req.url);
|
||||
if (url.pathname === '/caption.json') {
|
||||
const lang = url.searchParams.get('lang') ?? '';
|
||||
|
|
@ -162,11 +164,11 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
return Response.json({ text: texts[Number(url.searchParams.get('i'))] ?? '', ink: style === 'app' ? '#111827' : pal.ink, rtl: rtl.get(lang),
|
||||
fonts: { title: resolveShotFonts(shots, style, ctx.loaded.resolve, root).title.family, script: script ? `Store ${script.slug}` : null, titleStack: readManifest(fontsDir)?.langs[lang]?.title } });
|
||||
}
|
||||
if (url.pathname === '/caption.html') return new Response(Bun.file(join(import.meta.dir, 'caption.html')), { headers: { 'content-type': 'text/html; charset=utf-8' } });
|
||||
if (url.pathname === '/caption.html') return new Response(SHOTS_PAGES['caption.html']!, { headers: { 'content-type': 'text/html; charset=utf-8' } });
|
||||
if (url.pathname.startsWith('/fonts/')) {
|
||||
const file = fontsRoute(decodeURIComponent(url.pathname.slice(7)), fontsDir, root, readManifest(fontsDir));
|
||||
const f = file ? Bun.file(file) : null;
|
||||
if (f && await f.exists()) return new Response(f, { headers: { 'content-type': FONT_TYPES[file!.split('.').pop()!.toLowerCase()] ?? 'font/woff2' } });
|
||||
const f = file ? rt.fileResponse(file, { 'content-type': FONT_TYPES[file.split('.').pop()!.toLowerCase()] ?? 'font/woff2' }) : null;
|
||||
if (f) return f;
|
||||
}
|
||||
return new Response('no', { status: 404 });
|
||||
} });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue