@raven-js/soar
Version: 
Zero-dependency deployment tool for modern JavaScript - deploy any artifact to any target
147 lines (131 loc) • 4.62 kB
JavaScript
/**
 * @author Anonyfox <max@anonyfox.com>
 * @license MIT
 * @see {@link https://github.com/Anonyfox/ravenjs}
 * @see {@link https://ravenjs.dev}
 * @see {@link https://anonyfox.com}
 */
/**
 * @file Main entry point for the @raven-js/soar library.
 *
 * Soar provides surgical precision deployment capabilities for modern
 * JavaScript applications. Deploy any artifact (binary, script, static)
 * to any target (VPS, serverless, CDN) using zero external dependencies
 * and pure platform primitives.
 */
import { selectArtifact } from "./src/config/artifacts/select.js";
import { SoarConfig } from "./src/config/soar-config.js";
import { selectTarget } from "./src/config/targets/select.js";
/**
 * @typedef {Object} DeploymentResult
 * @property {boolean} success - Whether deployment succeeded
 * @property {string} [url] - Deployed URL if available
 * @property {string} [message] - Deployment message
 * @property {string} [scriptName] - Script name for Workers
 * @property {string} [deployedAt] - Deployment timestamp
 * @property {number} [filesUploaded] - Number of files uploaded
 */
/**
 * Deploys an artifact to a target using the provided configuration.
 *
 * @param {string|import('./src/config/soar-config.js').SoarConfigObject} config - Path to config file or config object
 * @param {string} [exportName] - Named export to use from config file
 * @returns {Promise<DeploymentResult>} Deployment result
 *
 * @example
 * ```javascript
 * // Deploy using config file
 * const result = await deploy('./soar.config.js');
 *
 * // Deploy using config object
 * const result = await deploy({
 *   artifact: { type: 'static', path: './dist' },
 *   target: { name: 'cloudflare-workers', scriptName: 'my-app' }
 * });
 * ```
 */
export async function deploy(config, exportName) {
	// Load and validate configuration
	const soarConfig =
		typeof config === "string"
			? await SoarConfig.fromFile(config, exportName)
			: new SoarConfig(
					/** @type {import('./src/config/soar-config.js').SoarConfigObject} */ (
						config
					),
				);
	const errors = soarConfig.validate();
	if (errors.length > 0) {
		throw new Error(`Configuration validation failed:\n${errors.join("\n")}`);
	}
	// Prepare artifact
	const artifact = selectArtifact(
		/** @type {any} */ (soarConfig.getArtifact()),
	);
	await artifact.prepare();
	// Deploy to target
	const target = selectTarget(/** @type {any} */ (soarConfig.getTarget()));
	const result = await target.deploy(artifact);
	return /** @type {DeploymentResult} */ (result);
}
/**
 * @typedef {Object} DeploymentPlan
 * @property {Object} artifact - Artifact information
 * @property {string} artifact.type - Artifact type
 * @property {string} artifact.path - Artifact path
 * @property {Object} [artifact.manifest] - File manifest if available
 * @property {Object} target - Target information
 * @property {string} target.type - Target type
 * @property {Object} target.config - Target configuration
 * @property {boolean} planned - Always true for plans
 * @property {string} timestamp - Plan timestamp
 */
/**
 * Plans a deployment without executing it (dry-run).
 *
 * @param {string|import('./src/config/soar-config.js').SoarConfigObject} config - Path to config file or config object
 * @param {string} [exportName] - Named export to use from config file
 * @returns {Promise<DeploymentPlan>} Deployment plan
 */
export async function plan(config, exportName) {
	// Load and validate configuration
	const soarConfig =
		typeof config === "string"
			? await SoarConfig.fromFile(config, exportName)
			: new SoarConfig(
					/** @type {import('./src/config/soar-config.js').SoarConfigObject} */ (
						config
					),
				);
	const errors = soarConfig.validate();
	if (errors.length > 0) {
		throw new Error(`Configuration validation failed:\n${errors.join("\n")}`);
	}
	// Prepare artifact for planning
	const artifact = selectArtifact(
		/** @type {any} */ (soarConfig.getArtifact()),
	);
	await artifact.prepare();
	const target = selectTarget(/** @type {any} */ (soarConfig.getTarget()));
	// Cast to StaticArtifact since we know it has getManifest method
	const staticArtifact =
		/** @type {import('./src/config/artifacts/static.js').StaticArtifact} */ (
			artifact
		);
	// Return deployment plan
	return {
		artifact: {
			type: artifact.constructor.name,
			path: artifact.getPath(),
			...(staticArtifact.getManifest
				? { manifest: await staticArtifact.getManifest() }
				: {}),
		},
		target: {
			type: target.constructor.name,
			config: soarConfig.getTarget(),
		},
		planned: true,
		timestamp: new Date().toISOString(),
	};
}