import { n as fail } from "./exports-CWOtdr_a.js";
import { t as private_env } from "./shared-server-Cha0Qlv9.js";
import { t as Exception } from "./const-Dy5RfQqP.js";
import { n as PUBLIC_MODE, r as formatFrom, t as HttpClient } from "./format-DGOlJxF5.js";
import { a as getProject, i as getOrCreateSession, l as tryAddProjectSession, o as resolveProject, r as generateKey, s as resolveSession, t as addProjectToSession } from "./session-DLCjVGKo.js";
import { plainToInstance } from "class-transformer";
import { validate } from "class-validator";
import path, { dirname, join, resolve } from "node:path";
import fs, { existsSync } from "node:fs";
import { $ } from "bun";
import { config } from "dotenv";
import ts from "typescript";
//#region .svelte-kit/adapter-bun/chunks/request-handler.js
var parseFormDataBody = (rawBody, c) => {
	return parseBody(Object.fromEntries(Array.from(rawBody.entries()).map(([key, value]) => [key, JSON.parse(value)])), c);
};
var parseBody = async (baseBody, c) => {
	if (!c) return baseBody;
	const errors = await validate(plainToInstance(c, baseBody, { excludeExtraneousValues: true }));
	if (errors.length > 0) throw new Exception("Bad Request", `Invalid body :\n${errors.toString().replace(/,/g, "\n")}`, 400);
	return baseBody;
};
var getContext = async (event) => {
	const session = await resolveSession(event.locals.session);
	const project = resolveProject(event.request.headers, session);
	return {
		online: PUBLIC_MODE === "ONLINE",
		session,
		project
	};
};
var setTokensInCookies = (cookies, { accessToken, refreshToken, tokenExpiresAt }) => {
	cookies.set("accessToken", accessToken, {
		httpOnly: true,
		secure: private_env.NODE_ENV === "production",
		sameSite: "lax",
		path: "/",
		expires: new Date(tokenExpiresAt)
	});
	cookies.set("refreshToken", refreshToken, {
		httpOnly: true,
		secure: private_env.NODE_ENV === "production",
		sameSite: "lax",
		path: "/",
		maxAge: 3600 * 24 * 30
	});
};
var refreshToken = async (cookies, force = false) => {
	let accessToken = force ? void 0 : cookies.get("accessToken");
	const refreshToken = cookies.get("refreshToken");
	if (!accessToken) {
		if (!refreshToken) throw new Exception("Unauthorized", "No token found", 401);
		try {
			const tokens = await getNoAuthApi().auth.refreshToken({ refreshToken });
			setTokensInCookies(cookies, tokens);
			accessToken = tokens.accessToken;
		} catch {
			throw new Exception("Unauthorized", "Invalid refresh token", 401);
		}
	}
	return accessToken;
};
var getRequestParams = (params, token) => ({
	...params,
	options: {
		...params.options,
		headers: {
			...params.options.headers,
			Authorization: `Bearer ${token}`
		}
	}
});
var useTokenMiddleware = (cookies) => {
	return async (params, next) => {
		const res = await next(getRequestParams(params, cookies.get("accessToken") ?? await refreshToken(cookies)));
		if (res.status === 401) return await next(getRequestParams(params, await refreshToken(cookies, true)));
		return res;
	};
};
var BaseRepository = class {
	_client;
	_online;
	constructor(client, online = true) {
		this._client = client;
		this._online = online;
	}
	get(path, options) {
		return this.runRequest("get", path, options);
	}
	post(path, body, options) {
		return this.runRequestBody("post", path, body, options);
	}
	put(path, body, options) {
		return this.runRequestBody("put", path, body, options);
	}
	patch(path, body, options) {
		return this.runRequestBody("patch", path, body, options);
	}
	delete(path, options) {
		return this.runRequest("delete", path, options);
	}
	assertOnline() {
		if (!this._online) throw new Error("This route is only available in online mode");
	}
	async runRequest(request, path, options) {
		if (!options?.offline) this.assertOnline();
		const res = await this._client[request](path, options);
		if (!res.ok) throw new Error(`Request failed with status code ${res.status}`, { cause: await res.json() });
		return await res.json();
	}
	async runRequestBody(request, path, body, options) {
		if (!options?.offline) this.assertOnline();
		const res = await this._client[request](path, body === void 0 ? void 0 : JSON.stringify(body), options);
		const data = await res.json();
		if (!res.ok) throw new Error(`Request failed with status code ${res.status}`, { cause: data });
		return data;
	}
};
var AuthRepository = class extends BaseRepository {
	refreshToken(input) {
		return this.post(`/auth/refresh-token`, input);
	}
};
var ProjectRepository = class extends BaseRepository {
	getProject(id) {
		return this.get(`/editor/projects/${id}`);
	}
	getProjects() {
		return this.get(`/projects`);
	}
};
var RegistryRepository = class extends BaseRepository {
	getPackage(packageName) {
		return this.get(`/registry/${packageName}`, { offline: true });
	}
};
var DEFAULT_API_URL = "https://api.nanoforge.eu";
var getNoAuthApi = () => {
	const client = new HttpClient(private_env.API_URL ?? DEFAULT_API_URL, { headers: { "Content-Type": "application/json" } });
	const isOnline = PUBLIC_MODE === "ONLINE";
	return {
		auth: new AuthRepository(client, isOnline),
		registry: new RegistryRepository(client, isOnline)
	};
};
var getApi = (cookies) => {
	if (!private_env.API_KEY) throw new Error("API_KEY is not defined");
	const client = new HttpClient(private_env.API_URL ?? DEFAULT_API_URL, { headers: {
		"Content-Type": "application/json",
		"Api-Key": private_env.API_KEY
	} }).useMiddlewares(useTokenMiddleware(cookies));
	return {
		auth: new AuthRepository(client),
		projects: new ProjectRepository(client),
		registry: new RegistryRepository(client)
	};
};
var CLI_NEW_DEFAULTS = {
	packageManager: "npm",
	language: "ts",
	strict: false,
	server: false,
	skipInstall: false,
	docker: false,
	git: false,
	gitRemote: false
};
var CLI_INSTALL_DEFAULTS = {};
var CLI_BUILD_DEFAULTS = {};
var CLI_START_DEFAULTS = {};
var CLI_DEV_DEFAULTS = {};
var CLI_GENERATE_DEFAULTS = {};
var CLI_CREATE_DEFAULTS = {};
var CliError = class extends Exception {
	constructor(message) {
		super("Internal Server Error", message, 500);
	}
};
var Cli = class {
	_projectPath;
	constructor(context) {
		this._projectPath = context.project?.path;
	}
	new(opts, runOpts) {
		return this.runCommand("new", [], { ...opts }, CLI_NEW_DEFAULTS, runOpts);
	}
	install(pkgs, opts, runOpts) {
		this.assertProject();
		return this.runCommand("install", pkgs, {
			...opts,
			directory: this._projectPath
		}, CLI_INSTALL_DEFAULTS, runOpts);
	}
	build(opts, runOpts) {
		this.assertProject();
		return this.runCommand("build", [], {
			...opts,
			directory: this._projectPath
		}, CLI_BUILD_DEFAULTS, runOpts);
	}
	start(opts, runOpts) {
		this.assertProject();
		return this.runCommand("start", [], {
			...opts,
			directory: this._projectPath
		}, CLI_START_DEFAULTS, runOpts);
	}
	dev(opts, runOpts) {
		this.assertProject();
		return this.runCommand("dev", [], {
			...opts,
			directory: this._projectPath
		}, CLI_DEV_DEFAULTS, runOpts);
	}
	generate(opts, runOpts) {
		this.assertProject();
		return this.runCommand("generate", [], {
			...opts,
			directory: this._projectPath
		}, CLI_GENERATE_DEFAULTS, runOpts);
	}
	create(part, opts, runOpts) {
		this.assertProject();
		return this.runCommand("create", [part], {
			...opts,
			directory: this._projectPath
		}, CLI_CREATE_DEFAULTS, runOpts);
	}
	runCommand(command, params, opts, defaultOpts, { async = false } = {}) {
		const fullCommand = [
			private_env.NF_CLI_PATH ?? "nf",
			command,
			...params,
			...this.resolveParams(opts, defaultOpts)
		];
		if (async) {
			const res = Bun.spawn(fullCommand, {
				stdout: "pipe",
				stderr: "pipe",
				cwd: private_env.FS_ROOT ?? void 0
			});
			res.exited.then((exitCode) => {
				if (exitCode !== 0) {
					console.log(res.stdout.toString());
					console.error(res.stderr.toString());
					throw new CliError(res.stderr.toString());
				}
			});
		} else {
			const res = Bun.spawnSync(fullCommand, {
				stdout: "pipe",
				stderr: "pipe",
				cwd: private_env.FS_ROOT ?? void 0
			});
			if (res.exitCode !== 0) {
				console.log(res.stdout.toString());
				console.error(res.stderr.toString());
				throw new CliError(res.stderr.toString());
			}
		}
	}
	mergeParams(...opts) {
		const merged = {};
		for (const opt of opts) for (const [key, value] of Object.entries(opt)) {
			if (value === void 0) continue;
			merged[key] = value;
		}
		return merged;
	}
	resolveParams(opts, defaultOpts) {
		const params = [];
		for (const [key, value] of Object.entries(this.mergeParams(defaultOpts, opts))) {
			const name = formatFrom.camel(key).toKebab();
			if (typeof value === "boolean") if (value) params.push(`--${name}`);
			else params.push(`--no-${name}`);
			else params.push(`--${name}`, `${value}`);
		}
		return params;
	}
	assertProject() {
		if (!this._projectPath) throw new CliError("No project path set");
		if (!Bun.file(this._projectPath).exists()) throw new CliError("Project does not exist");
	}
};
var resolveRootPath = (userPath) => {
	return resolve(resolve(private_env.FS_ROOT ?? ""), userPath);
};
var FileSystemError = class extends Exception {
	constructor(message) {
		super("Bad Request", message, 400);
	}
};
function directoryContentToFileEntries(directoryContent, basePath = "") {
	const entries = [];
	directoryContent.files.forEach((file) => {
		entries.push(basePath + "/" + file);
	});
	Object.entries(directoryContent.directories).forEach(([path, dirContent]) => {
		if (!dirContent) return;
		entries.push(...directoryContentToFileEntries(dirContent, basePath + "/" + path));
	});
	return entries;
}
var ProjectDirectory = class {
	path;
	projectPath;
	constructor(dirPath, projectPath) {
		this.path = resolve(projectPath, "./" + dirPath);
		this.projectPath = projectPath;
		this._checkPathIsInsideProject();
	}
	read(recursive = false) {
		this._checkPathExists();
		this._checkPathIsDir();
		this._checkPathIsReadable();
		return this._readDirContent(this.path, recursive);
	}
	create() {
		this._checkPathNotExists();
		fs.mkdirSync(this.path, { recursive: true });
	}
	delete(recursive = false) {
		this._checkPathExists();
		this._checkPathIsDir();
		if (!recursive) this._checkDirIsEmpty();
		fs.rmSync(this.path, { recursive });
	}
	rename(newPath) {
		const absoluteNewDirPath = resolve(this.projectPath, "./" + newPath);
		this._checkPathIsInsideProject(absoluteNewDirPath);
		this._checkPathExists();
		this._checkPathIsDir();
		this._checkPathIsWritable();
		const newFolderPath = dirname(absoluteNewDirPath);
		this._checkPathExists(newFolderPath);
		this._checkPathIsWritable(newFolderPath);
		this._checkPathNotExists(absoluteNewDirPath);
		fs.renameSync(this.path, absoluteNewDirPath);
		this.path = absoluteNewDirPath;
	}
	assertExists() {
		this._checkPathExists();
		this._checkPathIsDir();
		this._checkPathIsReadable();
	}
	_checkPathIsInsideProject(path = this.path) {
		if (!path.startsWith(this.projectPath)) throw new FileSystemError(`Path ${path} is outside of the project directory`);
	}
	_checkPathExists(path = this.path) {
		if (!fs.existsSync(path)) throw new FileSystemError(`Path ${path} should exist`);
	}
	_checkPathNotExists(path = this.path) {
		if (fs.existsSync(path)) throw new FileSystemError(`Path ${path} should not exist`);
	}
	_checkPathIsDir(path = this.path) {
		let stats;
		try {
			stats = fs.lstatSync(path);
		} catch {
			throw new FileSystemError(`Path ${path} does not exist`);
		}
		if (!stats.isDirectory()) throw new FileSystemError(`Path ${path} is not a directory`);
	}
	_checkPathIsWritable(path = this.path) {
		try {
			fs.accessSync(path, fs.constants.W_OK);
		} catch {
			throw new FileSystemError(`Path ${path} writable`);
		}
	}
	_checkPathIsReadable(path = this.path) {
		try {
			fs.accessSync(path, fs.constants.R_OK);
		} catch {
			throw new FileSystemError(`Path ${path} writable`);
		}
	}
	_checkDirIsEmpty(path = this.path) {
		if (fs.readdirSync(path).length > 0) throw new FileSystemError(`Directory ${path} is not empty`);
	}
	_readDirContent(path = this.path, recursive = false) {
		const dirContent = {
			files: [],
			directories: {}
		};
		fs.readdirSync(path, {
			withFileTypes: true,
			recursive: false
		}).forEach((item) => {
			if (item.isFile()) dirContent.files.push(item.name);
			else if (item.isDirectory()) dirContent.directories[item.name] = recursive ? this._readDirContent(path + "/" + item.name, recursive) : null;
		});
		return dirContent;
	}
};
var ProjectFile = class {
	_path;
	projectPath;
	constructor(filePath, projectPath) {
		this._path = path.resolve(projectPath, "./" + filePath);
		this.projectPath = projectPath;
		this._checkPathIsInsideProject();
	}
	get path() {
		this._checkPathExists();
		return this._path;
	}
	read(encoding) {
		this.isReadable();
		return fs.readFileSync(this._path).toString(encoding);
	}
	readStream() {
		this.isReadable();
		return Bun.file(this._path).stream();
	}
	readJson() {
		const raw = this.read();
		return JSON.parse(raw);
	}
	write(text) {
		this._checkWritableAndCreateFolder();
		fs.writeFileSync(this._path, text, { flush: true });
	}
	writeJson(content) {
		const raw = JSON.stringify(content);
		this.write(raw);
	}
	getWriteStream() {
		this._checkWritableAndCreateFolder();
		return fs.createWriteStream(this._path);
	}
	delete() {
		this._checkPathExists();
		this._checkPathIsFile();
		this._checkPathIsWritable();
		fs.rmSync(this._path);
	}
	rename(newPath) {
		const absoluteNewPath = path.resolve(this.projectPath, "./" + newPath);
		this._checkPathIsInsideProject(newPath);
		this._checkPathExists();
		this._checkPathIsFile();
		this._checkPathIsWritable();
		const newFolderPath = path.dirname(absoluteNewPath);
		this._checkPathExists(newFolderPath);
		this._checkPathIsWritable(newFolderPath);
		this._checkPathNotExists(absoluteNewPath);
		fs.renameSync(this._path, absoluteNewPath);
		this._path = absoluteNewPath;
	}
	isReadable() {
		this._checkPathExists();
		this._checkPathIsFile();
		this._checkPathIsReadable();
	}
	_checkWritableAndCreateFolder() {
		const folderPath = path.dirname(this._path);
		try {
			this._checkPathExists(folderPath);
		} catch {
			fs.mkdirSync(folderPath, { recursive: true });
		}
		if (fs.existsSync(this._path)) {
			this._checkPathIsFile();
			this._checkPathIsWritable();
		} else {
			this._checkPathExists(folderPath);
			this._checkPathIsDir(folderPath);
			this._checkPathIsWritable(folderPath);
		}
	}
	_checkPathIsInsideProject(path = this._path) {
		if (!path.startsWith(this.projectPath)) throw new FileSystemError(`Path ${path} is outside of the project directory`);
	}
	_checkPathExists(path = this._path) {
		if (!fs.existsSync(path)) throw new FileSystemError(`Path ${path} should exist`);
	}
	_checkPathNotExists(path = this._path) {
		if (fs.existsSync(path)) throw new FileSystemError(`Path ${path} should not exist`);
	}
	_checkPathIsFile(path = this._path) {
		let stats;
		try {
			stats = fs.lstatSync(path);
		} catch {
			throw new FileSystemError(`Path ${path} does not exist`);
		}
		if (!stats.isFile()) throw new FileSystemError(`Path ${path} is not a this.path`);
	}
	_checkPathIsDir(path = this._path) {
		let stats;
		try {
			stats = fs.lstatSync(path);
		} catch {
			throw new FileSystemError(`Path ${path} does not exist`);
		}
		if (!stats.isDirectory()) throw new FileSystemError(`Path ${path} is not a directory`);
	}
	_checkPathIsWritable(path = this._path) {
		try {
			fs.accessSync(path, fs.constants.W_OK);
		} catch {
			throw new FileSystemError(`Path ${path} writable`);
		}
	}
	_checkPathIsReadable(path = this._path) {
		try {
			fs.accessSync(path, fs.constants.R_OK);
		} catch {
			throw new FileSystemError(`Path ${path} writable`);
		}
	}
};
var FileSystem = class {
	_rootPath;
	constructor(arg) {
		if (typeof arg === "string") {
			this._rootPath = resolveRootPath(arg);
			return;
		}
		if (!arg.project) throw new Exception("Bad Request", "Project is not defined", 400);
		this._rootPath = resolveRootPath(arg.project.path);
	}
	getDirectory(path) {
		return new ProjectDirectory(path, this._rootPath);
	}
	getFile(path) {
		return new ProjectFile(path, this._rootPath);
	}
};
var Git = class {
	_rootPath;
	constructor() {
		this._rootPath = resolve(private_env.FS_ROOT ?? "");
	}
	async clone(url, options) {
		const path = await this.resolvePath(url);
		if (existsSync(path)) return path;
		await this.runCommand("clone", [url, path], { ...options });
		return path;
	}
	async runCommand(command, params, options) {
		let sshPath;
		if (options?.sshKey) sshPath = await this.createSshKeyFile(options.sshKey);
		const cwd = resolve(this._rootPath, options?.path ?? "");
		const sshEnv = sshPath ? { GIT_SSH_COMMAND: `ssh -i ${sshPath}` } : {};
		try {
			await $`git ${command} ${params}`.cwd(cwd).env({
				...process.env,
				...sshEnv
			});
		} finally {
			if (sshPath) await this.deleteSshKeyFile(sshPath);
		}
	}
	async createSshKeyFile(sshKey) {
		const path = `/tmp/nanoforge/${generateKey()}`;
		await Bun.file(path).write(sshKey);
		return path;
	}
	async deleteSshKeyFile(path) {
		await Bun.file(path).delete();
	}
	async resolvePath(url) {
		const basePath = url.split("/").pop()?.replace(".git", "");
		if (!basePath) throw new Error("Invalid URL");
		const fullBasePath = resolve(this._rootPath, basePath);
		let path = fullBasePath;
		while (await Bun.file(path).exists()) path = resolve(fullBasePath, generateKey(5));
		return path;
	}
};
var loadProject = async (projectSession, handler) => {
	const session = await getOrCreateSession(handler.event.locals.session);
	const projectId = tryAddProjectSession(projectSession);
	addProjectToSession(session, projectId);
	handler.context = {
		...handler.context,
		project: projectSession
	};
	return {
		id: projectId,
		cacheResolvable: handler.context.online ? projectSession.gateway?.id : projectSession.path
	};
};
var loadProjectFromId = async (id, handler) => {
	const session = await getOrCreateSession(handler.event.locals.session);
	const projectSession = getProject(id, session);
	if (!projectSession) throw new Exception("Bad Request", "Project not found", 400);
	addProjectToSession(session, id);
	handler.context = {
		...handler.context,
		project: projectSession
	};
	return {
		id,
		cacheResolvable: handler.context.online ? projectSession.gateway?.id : projectSession.path
	};
};
var LibraryHandler = class {
	handler;
	constructor(handler) {
		this.handler = handler;
	}
	async installLibrary(name) {
		this.handler._cli.install([name], {
			server: this.handler._part === "server" || void 0,
			lib: true
		});
		return { save: {
			id: this._resolveIdFromName(name),
			type: "",
			name: "",
			path: name
		} };
	}
	_resolveIdFromName(name) {
		const [org, pkg] = name.split("/");
		if (!org || !pkg) throw new Error("Invalid library name");
		const id = !pkg ? org : pkg;
		return `${formatFrom.kebab(id).toCamel()}Library`;
	}
};
var PREFIX_CLIENT = "NANOFORGE_CLIENT_";
var PREFIX_SERVER = "NANOFORGE_SERVER_";
var PREFIX = "NANOFORGE_";
var loadEnv = (path) => {
	const env = {};
	config({
		path,
		processEnv: env
	});
	return env;
};
var parseEnv = (part, env) => {
	const partPrefix = part === "client" ? PREFIX_CLIENT : PREFIX_SERVER;
	return Object.fromEntries(Object.entries(env).filter(([key]) => key.startsWith(PREFIX) && (part === "client" && !key.startsWith(PREFIX_SERVER) || part === "server" && !key.startsWith(PREFIX_CLIENT))).map(([key, value]) => [key.replace(new RegExp(`^${partPrefix}|${PREFIX}`), ""), value]));
};
var resolveEnv = (part, projectPath) => {
	return parseEnv(part, loadEnv(resolve(projectPath, ".env")));
};
var Loader = class {
	handler;
	_fs;
	_basePath;
	constructor(handler) {
		this.handler = handler;
		this._basePath = join(this.handler._path, ".nanoforge", this.handler._part);
		this._fs = new FileSystem(this._basePath);
	}
	getEnv() {
		return resolveEnv(this.handler._part, this.handler._path);
	}
	getManifest() {
		return {
			version: "1.0.0",
			files: directoryContentToFileEntries(this._fs.getDirectory(".").read(true)).map((path) => ({ path }))
		};
	}
	getFile(path) {
		return this._fs.getFile(path);
	}
};
var PackageTypeEnum = /* @__PURE__ */ function(PackageTypeEnum) {
	PackageTypeEnum["COMPONENT"] = "component";
	PackageTypeEnum["SYSTEM"] = "system";
	return PackageTypeEnum;
}({});
var MANIFEST_TITLES = {
	[PackageTypeEnum.COMPONENT]: "EDITOR_COMPONENT_MANIFEST",
	[PackageTypeEnum.SYSTEM]: "EDITOR_SYSTEM_MANIFEST"
};
var findManifestNode = (title, source) => {
	let res = null;
	source.forEachChild((node) => {
		if (!ts.isVariableStatement(node)) return;
		node.declarationList.declarations.forEach((decl) => {
			if (!ts.isVariableDeclaration(decl)) return;
			if (decl.name.getText() === title) res = decl;
		});
	});
	return res;
};
var getName = (source) => {
	let res = null;
	source.forEachChild((node) => {
		if (!ts.isExportAssignment(node)) return;
		if (!node.getChildren().some((n) => n.getText() === "export") || !node.getChildren().some((n) => n.getText() === "default")) return;
		res = node.expression.getFirstToken()?.getText();
	});
	return res;
};
var parseProperty = (prop) => {
	if (!ts.isPropertyAssignment(prop)) return {};
	const name = prop.name.getText();
	const value = prop.initializer;
	if (!value) return {};
	return { [name]: parseElement(value) };
};
var parseElement = (value) => {
	if (ts.isStringLiteral(value)) return value.text;
	if (ts.isNumericLiteral(value)) return Number(value.text);
	if (ts.isLiteralTypeLiteral(value)) {
		const txt = value.getText();
		if (txt === "true") return true;
		if (txt === "false") return false;
		if (txt === "null") return null;
		if (txt === "undefined") return void 0;
	}
	if (ts.isArrayLiteralExpression(value)) return value.elements.map((el) => parseElement(el));
	if (ts.isObjectLiteralExpression(value)) return value.properties.reduce((acc, prop) => {
		return {
			...acc,
			...parseProperty(prop)
		};
	}, {});
	throw new Error("Unknown element type");
};
var getManifestFromNode = (source) => {
	if (!source) return null;
	const init = source.initializer;
	if (!init) return null;
	if (!ts.isObjectLiteralExpression(init)) return null;
	return parseElement(init);
};
var parseManifest = (title, source) => {
	const id = getName(source);
	const manifest = getManifestFromNode(findManifestNode(title, source));
	if (!id || !manifest) return null;
	return {
		id,
		...manifest
	};
};
var resolveManifest = (type, content) => {
	const source = ts.createSourceFile("tmp.ts", content, ts.ScriptTarget.ESNext, true);
	return parseManifest(MANIFEST_TITLES[type], source);
};
var PackageHandler = class {
	handler;
	constructor(handler) {
		this.handler = handler;
	}
	async installComponent(name) {
		const rc = await this.handler._api.registry.getPackage(name);
		if (rc.type !== "component") throw new Error(`Can only add component: ${name} is a ${rc.type}`);
		this.handler._cli.install([name], { server: this.handler._part === "server" || void 0 });
		return this._getNewComponentPackage(rc.name, rc._file);
	}
	async installSystem(name) {
		const rs = await this.handler._api.registry.getPackage(name);
		if (rs.type !== "system") throw new Error(`Can only add system: ${name} is a ${rs.type}`);
		this.handler._cli.install([name], { server: this.handler._part === "server" || void 0 });
		return this._getNewSystemPackage(rs.name, rs._file);
	}
	/**
	* Create a new component in the project
	* @beta function to be reworked
	*
	* @param {string} name - Name of the component
	*/
	createComponent(name) {
		this._createPackage(PackageTypeEnum.COMPONENT, name);
		return this._getNewComponentPackage(formatFrom.all(name).toPascal() + "Component", formatFrom.all(name).toKebab() + ".component");
	}
	/**
	* Create a new system in the project and update the save file
	* @beta function to be reworked
	*
	* @param {string} name - Name of the system
	*/
	createSystem(name) {
		this._createPackage(PackageTypeEnum.SYSTEM, name);
		return this._getNewSystemPackage(formatFrom.all(name).toCamel() + "System", formatFrom.all(name).toKebab() + ".system");
	}
	/**
	* Get the manifest of the component
	* @beta function to be reworked
	*
	* @param {string} path - Path from `/<client|server>`
	*
	* @returns Manifest of the component
	*/
	getComponentManifest(path) {
		return this._getPackageManifest(PackageTypeEnum.COMPONENT, path);
	}
	/**
	* Get the manifest of the system
	* @beta function to be reworked
	*
	* @param {string} path - Path from `/<client|server>`
	*
	* @returns Manifest of the system
	*/
	getSystemManifest(path) {
		return this._getPackageManifest(PackageTypeEnum.SYSTEM, path);
	}
	async getComponents() {
		return this._resolvesPackages(PackageTypeEnum.COMPONENT);
	}
	async getSystems() {
		return this._resolvesPackages(PackageTypeEnum.SYSTEM);
	}
	_resolvesPackages(type) {
		const basePath = type === PackageTypeEnum.COMPONENT ? "./components" : "./systems";
		const content = this.handler.fs.getDirectory(basePath).read(true);
		return this._resolvesPackageFilesPathFromContent(content).map((path) => this._resolvesPackage(type, join(basePath, path)));
	}
	_resolvesPackage(type, path) {
		const manifest = this._getPackageManifest(type, path);
		const res = {
			manifest,
			save: {
				name: manifest.id,
				path
			}
		};
		if (type === PackageTypeEnum.COMPONENT) res.save.params = manifest.params.map(({ name }) => name);
		return res;
	}
	_resolvesPackageFilesPathFromContent(content, path = "") {
		const files = content.files.map((file) => join(path, file));
		const directories = Object.entries(content.directories).map(([key, value]) => {
			if (!value) return [];
			return this._resolvesPackageFilesPathFromContent(value, join(path, key)).filter(Boolean);
		});
		return [...files, ...directories.flat()];
	}
	_getNewComponentPackage(name, fileName) {
		const path = `./components/${fileName}`;
		const manifest = this._findPackageManifest((p) => this.getComponentManifest(p), path);
		return {
			manifest,
			save: {
				name,
				path,
				paramsNames: manifest.params.map(({ name }) => name)
			}
		};
	}
	_getNewSystemPackage(name, fileName) {
		const path = `./systems/${fileName}`;
		return {
			manifest: this._findPackageManifest((p) => this.getSystemManifest(p), path),
			save: {
				name,
				path
			}
		};
	}
	_findPackageManifest(manifestGetter, path) {
		const manifest = [
			"",
			".ts",
			".js"
		].reduce((result, p) => {
			if (result) return result;
			try {
				return manifestGetter(path + p);
			} catch (e) {
				if (!(e instanceof FileSystemError)) throw e;
			}
		}, void 0);
		if (!manifest) throw new FileSystemError("Can't find package manifest");
		return manifest;
	}
	_createPackage(type, name) {
		this.handler._cli.create(type, {
			name,
			server: this.handler._part === "server" || void 0
		});
	}
	_getPackageManifest(type, path) {
		return resolveManifest(type, this.handler._rootFs.getFile(this._resolvePartPath(path)).read());
	}
	_resolvePartPath(path) {
		return join(this.handler._part, path);
	}
};
var SaveHandler = class {
	handler;
	constructor(handler) {
		this.handler = handler;
	}
	async getSave() {
		return this.handler._rootFs.getFile(this.resolveSavePath()).readJson();
	}
	async updateSave(save) {
		this.handler._rootFs.getFile(this.resolveSavePath()).writeJson(save);
	}
	resolveSavePath() {
		return `.nanoforge/${this.handler._part}.save.json`;
	}
};
var ProjectHandler = class {
	_path;
	_part;
	_cli;
	_api;
	_rootFs;
	_fs;
	_loader;
	_save;
	_package;
	_library;
	constructor(handler, context, part) {
		if (!context.project) throw new Exception("Bad Request", "Project missing in context", 400);
		this._path = context.project.path;
		this._part = part;
		this._cli = handler.cli;
		this._api = handler.api;
		this._rootFs = handler.fs;
	}
	/**
	* Get the file system of the project content
	* This file system has his root at /<client|server>
	*/
	get fs() {
		if (!this._fs) this._fs = new FileSystem(join(this._path, this._part));
		return this._fs;
	}
	/**
	* Get the loader functions of the project
	*/
	get loader() {
		if (!this._loader) this._loader = new Loader(this);
		return this._loader;
	}
	get save() {
		if (!this._save) this._save = new SaveHandler(this);
		return this._save;
	}
	get package() {
		if (!this._package) this._package = new PackageHandler(this);
		return this._package;
	}
	get library() {
		if (!this._library) this._library = new LibraryHandler(this);
		return this._library;
	}
};
var ProjectManager = class {
	_handler;
	_context;
	_client;
	_server;
	constructor(handler, context) {
		this._handler = handler;
		this._context = context;
	}
	/**
	* Get the client project handler
	* @beta The separation between client and server projects is not the final goal of the project manager.
	*/
	get client() {
		if (!this._client) this._client = new ProjectHandler(this._handler, this._context, "client");
		return this._client;
	}
	/**
	* Get the server project handler
	* @beta The separation between client and server projects is not the final goal of the project manager.
	*/
	get server() {
		if (!this._server) this._server = new ProjectHandler(this._handler, this._context, "server");
		return this._server;
	}
};
var Handler = class {
	_context;
	_event;
	_body;
	_apiCache;
	_cliCache;
	_fsCache;
	_gitCache;
	_projectCache;
	constructor(context, event, body) {
		this._context = context;
		this._event = event;
		this._body = body;
	}
	get context() {
		return this._context;
	}
	set context(context) {
		this._context = context;
		this._apiCache = void 0;
		this._cliCache = void 0;
		this._fsCache = void 0;
		this._gitCache = void 0;
	}
	get event() {
		return this._event;
	}
	get body() {
		return this._body;
	}
	get api() {
		if (!this._apiCache) this._apiCache = this._context.online ? getApi(this._event.cookies) : getNoAuthApi();
		return this._apiCache;
	}
	get cli() {
		if (!this._cliCache) this._cliCache = new Cli(this._context);
		return this._cliCache;
	}
	get fs() {
		if (!this._fsCache) this._fsCache = new FileSystem(this._context);
		return this._fsCache;
	}
	get git() {
		if (!this._gitCache) this._gitCache = new Git();
		return this._gitCache;
	}
	get project() {
		if (!this._projectCache) this._projectCache = new ProjectManager(this, this._context);
		return this._projectCache;
	}
};
var DEFAULT_OPTIONS = {
	onlineOnly: false,
	offlineOnly: false,
	projectOptional: false
};
var assertProject = (context, options) => {
	if (options.projectOptional) return;
	if (!context.session) throw new Exception("Bad Request", "No session", 400);
	if (!context.project) throw new Exception("Bad Request", "Request required a project", 400);
	if (context.online && !context.project.gateway) throw new Exception("Internal Server Error", "Project don't have gateway on online mode", 500);
};
var assertMode = (context, options) => {
	if (options.onlineOnly && !context.online) throw new Exception("Method Not Allowed", "This action is not available offline", 405);
	if (options.offlineOnly && context.online) throw new Exception("Method Not Allowed", "This action is not available online", 405);
};
var assertRequest = (context, rawOptions) => {
	const options = {
		...DEFAULT_OPTIONS,
		...rawOptions
	};
	assertMode(context, options);
	assertProject(context, options);
};
var handleError$1 = (e) => {
	console.error(e);
	if (e instanceof Exception) return fail(e.status, {
		error: e.error,
		message: e.message
	});
	return fail(500, {
		error: "Internal Server Error",
		message: e.toString()
	});
};
var useActionHandler = (callback, options) => {
	return async (event) => {
		try {
			const context = await getContext(event);
			const body = await parseFormDataBody(await event.request.formData(), options?.body);
			assertRequest(context, options);
			return await callback(new Handler(context, event, body));
		} catch (e) {
			return handleError$1(e);
		}
	};
};
var handleError = (e) => {
	if (e instanceof Exception) return Response.json({
		error: e.error,
		message: e.message
	}, { status: e.status });
	return Response.json({
		error: "Internal Server Error",
		message: e
	}, { status: 500 });
};
var useRequestHandler = (callback, options) => {
	return async (event) => {
		try {
			const context = await getContext(event);
			assertRequest(context, options);
			return await callback(new Handler(context, event, {}));
		} catch (e) {
			return handleError(e);
		}
	};
};
//#endregion
export { useRequestHandler as i, loadProjectFromId as n, useActionHandler as r, loadProject as t };

//# sourceMappingURL=request-handler-guINVc1G.js.map