All files / blong-gogo/src runServer.ts

35.1% Statements 33/94
100% Branches 0/0
0% Functions 0/2
35.1% Lines 33/94

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 951x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                             1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                                                                                
import {type SolutionFactory} from '@feasibleone/blong/types';
import {existsSync} from 'node:fs';
import {basename, resolve} from 'node:path';
import {analyzeFolder, synthesizeServerFromHandlers} from './folderAnalysis.ts';
import load from './loadServer.ts';
 
/** Default intents applied when none are provided on the CLI. */
export const DEFAULT_INTENTS = ['microservice', 'integration', 'dev'] as const;
 
/**
 * Runs the standard platform lifecycle: start → test → (CI) stop.
 *
 * @param intents - Active intents that control which config blocks and layers are activated.
 *   Defaults to {@link DEFAULT_INTENTS} when omitted.
 */
export async function runPlatform(
    serverDef: SolutionFactory,
    name: string,
    intents: string[] = [...DEFAULT_INTENTS],
): Promise<void> {
    const platform = await load(
        serverDef as unknown as Parameters<typeof load>[0],
        name,
        name,
        intents,
    );
    await platform.start!({});
    await platform.test!(undefined);
    if (process.env.CI) await platform.stop!();
}
 
/**
 * Auto-detects and runs the appropriate platform from the given working directory.
 *
 * Resolution order:
 *   1. Explicit `target` path (loaded directly).
 *   2. `index.ts` — suite / realm with a custom runner.
 *   3. `server.ts` + `browser.ts` — two-platform suite.
 *   4. `server.ts` alone — server-only suite or realm.
 *   5. Folder contains handler files — synthesize a server suite on the fly.
 *   6. Throws if nothing matched.
 *
 * @param options.intents - Active intents from the CLI (positional args after the optional target
 *   path). When empty, {@link DEFAULT_INTENTS} are used so that a plain `blong` invocation works
 *   out of the box.
 */
export async function autoRun(options: {
    cwd: string;
    target?: string;
    intents?: string[];
}): Promise<void> {
    const {cwd, target, intents: cliIntents} = options;
    // Use CLI-supplied intents; fall back to defaults when the user passed none.
    const intents = cliIntents && cliIntents.length > 0 ? cliIntents : [...DEFAULT_INTENTS];

    if (target && existsSync(target)) {
        (await import(target)).default(load);
        return;
    }

    const name = basename(cwd);
    const indexFile = resolve(cwd, 'index.ts');
    const serverFile = resolve(cwd, 'server.ts');
    const browserFile = resolve(cwd, 'browser.ts');

    if (existsSync(indexFile)) {
        (await import(indexFile)).default(load);
    } else if (existsSync(serverFile) && existsSync(browserFile)) {
        const {default: serverDef} = await import(serverFile);
        const {default: browserDef} = await import(browserFile);
        const platforms: Awaited<ReturnType<typeof load>>[] = await Promise.all([
            load(serverDef, name, name, intents),
            load(browserDef, name, name, intents),
        ]);
        for (const platform of platforms) await platform.start({});
        await platforms[1].test!(undefined);
        if (process.env.CI) for (const platform of platforms) await platform.stop();
    } else if (existsSync(serverFile)) {
        const {default: serverDef} = await import(serverFile);
        await runPlatform(serverDef, name, intents);
    } else {
        const analysis = await analyzeFolder(cwd);
        if (analysis.kind === 'handlers' || analysis.kind === 'mixed') {
            await runPlatform(await synthesizeServerFromHandlers(cwd, analysis), name, intents);
        } else {
            throw new Error(
                `No entry point found in ${cwd}. ` +
                    'Run blong from a folder that contains a suite (server.ts / browser.ts / index.ts), ' +
                    'a realm (realm.ts), or handler files (e.g. helloHello.ts). ' +
                    'You can also provide a file path as an argument.',
            );
        }
    }
}