import { r as __toESM, t as __commonJSMin } from "./chunk-ByNZkrT6.js";
import { a as redirect } from "./exports-CWOtdr_a.js";
import { t as private_env } from "./shared-server-Cha0Qlv9.js";
import { n as SESSION_PROJECT_HEADER, t as Exception } from "./const-Dy5RfQqP.js";
import crypto$1 from "node:crypto";
//#region .svelte-kit/adapter-bun/chunks/session.js
/**
* Cookie signing and unsigning.
*
* Implemented with web crypto instead of Node.js crypto
* to work in Edge environments such as Cloudflare Worker.
*/
/**
* Convert a string to an ArrayBuffer
*
* @param {string} str string to convert to ArrayBuffer (should be the session id)
* @return {ArrayBuffer} buffer
*/
function str2ab(str) {
	const buffer = new Uint8Array(str.length);
	for (let i = 0; i < str.length; i += 1) buffer[i] = str.charCodeAt(i);
	return buffer;
}
/**
* Sign the given `val` with `secret`.
* The `secret` is a secret key to sign the cookie with.
* This is used to detect tampering the cookie later.
*
* The `secret` can be a string or an array of strings.
* If an array of strings is provided, only the first element will be used to sign the cookie,
*   but all the elements will be used to unsign the cookie.
*
* @param {string} val Cookie value to sign (should be the session id)
* @param {string | string[]} secret Secret string to sign with
* @return {Promise<string>} Signed cookie in the form of `<val>.<signature>` where `<signature>` is a base64 encoded HMAC SHA256 signature
*/
async function sign(val, secret) {
	const keyData = str2ab(typeof secret === "string" ? secret : secret[0]);
	const key = await crypto.subtle.importKey("raw", keyData, {
		name: "HMAC",
		hash: "SHA-256"
	}, false, ["sign"]);
	const signature = await crypto.subtle.sign("HMAC", key, str2ab(val));
	return `${val}.${btoa(String.fromCharCode.apply(null, Array.from(new Uint8Array(signature)))).replace(/=+$/, "")}`;
}
/**
* Unsign and decode the given `input` with `secret`,
*   returning `false` if the signature is invalid.
*
* The `secret` can be a string or an array of strings.
* If an array of strings is provided, only the first element will be used to sign the cookie,
*   but all the elements will be used to unsign the cookie.
*
* @param {string} input Signed cookie to unsign
* @param {string | string[]} secret Secret to sign with (should be the same as the one used to sign)
* @return {Promise<string | null>} Unsigned cookie or `null` if signature is invalid
*/
async function unsign(input, secret) {
	const tentativeValue = input.slice(0, input.lastIndexOf("."));
	if (typeof secret === "string") return await sign(tentativeValue, secret) === input ? tentativeValue : null;
	return (await Promise.all(secret.map(async (v) => {
		return await sign(tentativeValue, v) === input ? tentativeValue : null;
	}))).find((v) => v !== null) || null;
}
/**
* https://github.com/crypto-utils/uid-safe/tree/master
*
* Implemented with web crypto instead of Node.js crypto to work in Edge environments such as Cloudflare Worker.
*/
/**
* Generate a buffer with secure random bytes. *
* @param size The number of bytes to generate
* @returns buffer with secure random bytes
*/
var randomBytes = (size) => {
	if (size < 0) throw new RangeError("argument size must not be negative");
	return crypto.getRandomValues(new Uint8Array(size));
};
var EQUAL_END_REGEXP = /=+$/;
var PLUS_GLOBAL_REGEXP = /\+/g;
var SLASH_GLOBAL_REGEXP = /\//g;
var toString = (buf) => {
	return btoa(String.fromCharCode.apply(null, buf)).replace(EQUAL_END_REGEXP, "").replace(PLUS_GLOBAL_REGEXP, "-").replace(SLASH_GLOBAL_REGEXP, "_");
};
/**
* Generate a UID with the given length.
* @param length The number of bytes to generate
* @returns UID(base64 string)
*/
var uidSync = (length) => toString(randomBytes(length));
/**
* Generate a session ID.
* @returns {string} session ID(base64 string)
*/
var generateSessionId = () => uidSync(24);
/**
* Get default cookie options.
* @returns {CookieSerializeOptions & { path: string }} default cookie options
*/
var defaultCookieOptions = () => ({ path: "/" });
/**
* Get the TTL in milliseconds for the given cookie options.
* If `expires` and `maxAge` are set, then `maxAge` takes precedence.
* See *Note* below for details.
*
* *Note* the {@link https://tools.ietf.org/html/rfc6265#section-5.3|cookie storage model specification}
* states that if both `expires` and `maxAge` are set, then `maxAge` takes precedence, but it is
* possible not all clients by obey this, so if both are set, they should
* point to the same date and time.
*/
var getTtlMs = (cookie) => {
	const { expires, maxAge } = cookie;
	if (maxAge) return maxAge * 1e3;
	if (expires) {
		const ms = new Date(expires).getTime() - Date.now();
		return Math.ceil(ms / 1e3) * 1e3;
	}
	return Infinity;
};
/**
* Parse session cookie options.
* @param {SveltekitSessionConfig} options session options
* @param {SessionCookieOptions} cookieOptions session cookie options
* @returns {CookieSerializeOptions & { path: string }} cookie options
*/
var parseSessionCookieOptions = (options, cookieOptions) => {
	const cookie = { ...cookieOptions };
	if (options.cookie && options.cookie.encode) cookie.encode = (value) => options.cookie.encode(value);
	if (cookie.expires) cookie.expires = new Date(cookie.expires);
	return cookie;
};
var Session = class Session {
	constructor(event, options) {
		this.#id = generateSessionId();
		this.#cookieName = options.name || "connect.sid";
		this.#cookie = {
			...defaultCookieOptions(),
			...options.cookie
		};
		this.#sessionOptions = options;
		this.#event = event;
		this.#storeTtlMs = getTtlMs(options.cookie || {});
	}
	static async initialize(event, options) {
		const session = new Session(event, options);
		const { cookies } = session.#event;
		const { secret, store, rolling, saveUninitialized } = session.#sessionOptions;
		const unsignedSid = await unsign(cookies.get(session.#cookieName) || "", secret);
		if (unsignedSid) {
			const sessionData = await store.get(unsignedSid);
			if (sessionData) {
				session.#id = unsignedSid;
				session.#cookie = parseSessionCookieOptions(options, sessionData.cookie);
				session.#data = sessionData.data;
				session.#storeTtlMs = getTtlMs(sessionData.cookie);
				if (session.#cookie.maxAge && rolling) {
					await store.touch(session.#id, session.#cookie.maxAge * 1e3);
					cookies.set(session.#cookieName, await sign(session.#id, secret), session.#cookie);
				}
				return session;
			}
		}
		if (saveUninitialized) {
			await store.set(session.#id, {
				cookie: session.#getParsableCookieOptions(),
				data: session.#data
			}, session.#storeTtlMs);
			cookies.set(session.#cookieName, await sign(session.#id, secret), session.#cookie);
		}
		return session;
	}
	#id;
	#cookieName;
	#cookie;
	#data = {};
	#sessionOptions;
	#event;
	/**
	* ttl time(milliseconds) for session store.
	*/
	#storeTtlMs;
	/**
	* Session ID.
	*/
	get id() {
		return this.#id;
	}
	/**
	* Session cookie name.
	*/
	get cookieName() {
		return this.#cookieName;
	}
	/**
	* Session cookie options.
	*/
	get cookie() {
		return this.#cookie;
	}
	/**
	* Session data.
	*/
	get data() {
		return this.#data;
	}
	/**
	* Session store instance.
	*/
	get store() {
		return this.#sessionOptions.store;
	}
	#getParsableCookieOptions() {
		const cookie = { ...this.#cookie };
		if (cookie.encode) delete cookie.encode;
		return cookie;
	}
	/**
	* Set data in the session.
	*
	* If `saveUninitialized` is `true`, the session is saved without calling `save()`.
	* Conversely, if `saveUninitialized` is `false`, call `save()` to explicitly save the session.
	*/
	async setData(data) {
		this.#data = data;
		if (this.#sessionOptions.saveUninitialized) await this.store.set(this.id, {
			cookie: this.#getParsableCookieOptions(),
			data: this.#data
		}, this.#storeTtlMs);
	}
	/**
	* Save the session (save session to store) and set cookie.
	*/
	async save() {
		await this.#sessionOptions.store.set(this.id, {
			cookie: this.#getParsableCookieOptions(),
			data: this.#data
		}, this.#storeTtlMs);
		this.#event.cookies.set(this.#cookieName, await sign(this.#id, this.#sessionOptions.secret), this.#cookie);
	}
	/**
	* Regenerate the session simply invoke the method.
	* Once complete, a new Session and `Session` instance will be initialized.
	*/
	async regenerate() {
		await this.destroy();
		return new Session(this.#event, this.#sessionOptions);
	}
	/**
	* Destroy the session.
	*/
	async destroy() {
		await this.#sessionOptions.store.destroy(this.#id);
		this.#event.cookies.delete(this.#cookieName, { ...this.#cookie });
	}
};
var import_ttlcache = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
	/* istanbul ignore next */
	var perf = typeof performance === "object" && performance && typeof performance.now === "function" ? performance : Date;
	var now = () => perf.now();
	var isPosInt = (n) => n && n === Math.floor(n) && n > 0 && isFinite(n);
	var isPosIntOrInf = (n) => n === Infinity || isPosInt(n);
	module.exports = class TTLCache {
		constructor({ max = Infinity, ttl, updateAgeOnGet = false, checkAgeOnGet = false, noUpdateTTL = false, dispose, noDisposeOnSet = false } = {}) {
			this.expirations = Object.create(null);
			this.data = /* @__PURE__ */ new Map();
			this.expirationMap = /* @__PURE__ */ new Map();
			if (ttl !== void 0 && !isPosIntOrInf(ttl)) throw new TypeError("ttl must be positive integer or Infinity if set");
			if (!isPosIntOrInf(max)) throw new TypeError("max must be positive integer or Infinity");
			this.ttl = ttl;
			this.max = max;
			this.updateAgeOnGet = !!updateAgeOnGet;
			this.checkAgeOnGet = !!checkAgeOnGet;
			this.noUpdateTTL = !!noUpdateTTL;
			this.noDisposeOnSet = !!noDisposeOnSet;
			if (dispose !== void 0) {
				if (typeof dispose !== "function") throw new TypeError("dispose must be function if set");
				this.dispose = dispose;
			}
			this.timer = void 0;
			this.timerExpiration = void 0;
		}
		setTimer(expiration, ttl) {
			if (this.timerExpiration < expiration) return;
			if (this.timer) clearTimeout(this.timer);
			const t = setTimeout(() => {
				this.timer = void 0;
				this.timerExpiration = void 0;
				this.purgeStale();
				for (const exp in this.expirations) {
					this.setTimer(exp, exp - now());
					break;
				}
			}, ttl);
			/* istanbul ignore else - affordance for non-node envs */
			if (t.unref) t.unref();
			this.timerExpiration = expiration;
			this.timer = t;
		}
		cancelTimer() {
			if (this.timer) {
				clearTimeout(this.timer);
				this.timerExpiration = void 0;
				this.timer = void 0;
			}
		}
		/* istanbul ignore next */
		cancelTimers() {
			process.emitWarning("TTLCache.cancelTimers has been renamed to TTLCache.cancelTimer (no \"s\"), and will be removed in the next major version update");
			return this.cancelTimer();
		}
		clear() {
			const entries = this.dispose !== TTLCache.prototype.dispose ? [...this] : [];
			this.data.clear();
			this.expirationMap.clear();
			this.cancelTimer();
			this.expirations = Object.create(null);
			for (const [key, val] of entries) this.dispose(val, key, "delete");
		}
		setTTL(key, ttl = this.ttl) {
			const current = this.expirationMap.get(key);
			if (current !== void 0) {
				const exp = this.expirations[current];
				if (!exp || exp.length <= 1) delete this.expirations[current];
				else this.expirations[current] = exp.filter((k) => k !== key);
			}
			if (ttl !== Infinity) {
				const expiration = Math.floor(now() + ttl);
				this.expirationMap.set(key, expiration);
				if (!this.expirations[expiration]) {
					this.expirations[expiration] = [];
					this.setTimer(expiration, ttl);
				}
				this.expirations[expiration].push(key);
			} else this.expirationMap.set(key, Infinity);
		}
		set(key, val, { ttl = this.ttl, noUpdateTTL = this.noUpdateTTL, noDisposeOnSet = this.noDisposeOnSet } = {}) {
			if (!isPosIntOrInf(ttl)) throw new TypeError("ttl must be positive integer or Infinity");
			if (this.expirationMap.has(key)) {
				if (!noUpdateTTL) this.setTTL(key, ttl);
				const oldValue = this.data.get(key);
				if (oldValue !== val) {
					this.data.set(key, val);
					if (!noDisposeOnSet) this.dispose(oldValue, key, "set");
				}
			} else {
				this.setTTL(key, ttl);
				this.data.set(key, val);
			}
			while (this.size > this.max) this.purgeToCapacity();
			return this;
		}
		has(key) {
			return this.data.has(key);
		}
		getRemainingTTL(key) {
			const expiration = this.expirationMap.get(key);
			return expiration === Infinity ? expiration : expiration !== void 0 ? Math.max(0, Math.ceil(expiration - now())) : 0;
		}
		get(key, { updateAgeOnGet = this.updateAgeOnGet, ttl = this.ttl, checkAgeOnGet = this.checkAgeOnGet } = {}) {
			const val = this.data.get(key);
			if (checkAgeOnGet && this.getRemainingTTL(key) === 0) {
				this.delete(key);
				return;
			}
			if (updateAgeOnGet) this.setTTL(key, ttl);
			return val;
		}
		dispose(_, __) {}
		delete(key) {
			const current = this.expirationMap.get(key);
			if (current !== void 0) {
				const value = this.data.get(key);
				this.data.delete(key);
				this.expirationMap.delete(key);
				const exp = this.expirations[current];
				if (exp) if (exp.length <= 1) delete this.expirations[current];
				else this.expirations[current] = exp.filter((k) => k !== key);
				this.dispose(value, key, "delete");
				if (this.size === 0) this.cancelTimer();
				return true;
			}
			return false;
		}
		purgeToCapacity() {
			for (const exp in this.expirations) {
				const keys = this.expirations[exp];
				if (this.size - keys.length >= this.max) {
					delete this.expirations[exp];
					const entries = [];
					for (const key of keys) {
						entries.push([key, this.data.get(key)]);
						this.data.delete(key);
						this.expirationMap.delete(key);
					}
					for (const [key, val] of entries) this.dispose(val, key, "evict");
				} else {
					const s = this.size - this.max;
					const entries = [];
					for (const key of keys.splice(0, s)) {
						entries.push([key, this.data.get(key)]);
						this.data.delete(key);
						this.expirationMap.delete(key);
					}
					for (const [key, val] of entries) this.dispose(val, key, "evict");
					return;
				}
			}
		}
		get size() {
			return this.data.size;
		}
		purgeStale() {
			const n = Math.ceil(now());
			for (const exp in this.expirations) {
				if (exp === "Infinity" || exp > n) return;
				/* istanbul ignore next
				* mysterious need for a guard here?
				* https://github.com/isaacs/ttlcache/issues/26 */
				const keys = [...this.expirations[exp] || []];
				const entries = [];
				delete this.expirations[exp];
				for (const key of keys) {
					entries.push([key, this.data.get(key)]);
					this.data.delete(key);
					this.expirationMap.delete(key);
				}
				for (const [key, val] of entries) this.dispose(val, key, "stale");
			}
			if (this.size === 0) this.cancelTimer();
		}
		*entries() {
			for (const exp in this.expirations) for (const key of this.expirations[exp]) yield [key, this.data.get(key)];
		}
		*keys() {
			for (const exp in this.expirations) for (const key of this.expirations[exp]) yield key;
		}
		*values() {
			for (const exp in this.expirations) for (const key of this.expirations[exp]) yield this.data.get(key);
		}
		[Symbol.iterator]() {
			return this.entries();
		}
	};
})))(), 1);
var ONE_DAY_IN_SECONDS = 86400;
var MemoryStore = class {
	#sessions = new import_ttlcache.default();
	#prefix;
	#serializer;
	/**
	* Time to live in milliseconds.
	* default: 86400 * 1000
	*/
	#ttl;
	constructor(options) {
		this.#prefix = options?.prefix || "";
		this.#serializer = options?.serializer || JSON;
		this.#ttl = options?.ttl || ONE_DAY_IN_SECONDS * 1e3;
	}
	async get(id) {
		const key = this.#prefix + id;
		const storeData = this.#sessions.get(key);
		return storeData ? this.#serializer.parse(storeData) : null;
	}
	async set(id, storeData, ttl) {
		const key = this.#prefix + id;
		const serialized = this.#serializer.stringify(storeData);
		if (ttl !== Infinity) {
			this.#sessions.set(key, serialized, { ttl });
			return;
		}
		this.#sessions.set(key, serialized, { ttl: this.#ttl });
	}
	async destroy(id) {
		const key = this.#prefix + id;
		this.#sessions.delete(key);
	}
	async touch(id, ttl) {
		const key = this.#prefix + id;
		this.#sessions.setTTL(key, ttl);
	}
};
var memoryStore = new MemoryStore();
var sveltekitSessionHandle = (options) => async ({ event, resolve }) => {
	const { locals } = event;
	locals.session = await Session.initialize({ cookies: event.cookies }, {
		store: memoryStore,
		...options
	});
	return await resolve(event);
};
var generateKey = (size = 50) => {
	return crypto$1.randomBytes(size).toString("base64url").substring(0, size);
};
var PUBLIC_PATHS = ["/", "/load"];
var sessionStore = /* @__PURE__ */ new Map();
var isPublicPath = (url) => {
	return PUBLIC_PATHS.includes(url.pathname) || url.pathname.startsWith("/actions");
};
var resolveSession = async (sessionHandler) => {
	const id = sessionHandler.data.id;
	if (!id) return null;
	if (!sessionStore.has(id)) {
		await resetSession(sessionHandler);
		throw new Exception("Bad Request", "Invalid session", 400);
	}
	return sessionStore.get(id);
};
var isSessionExist = (id) => sessionStore.has(id);
var resetSession = (sessionHandler) => {
	return sessionHandler.destroy();
};
var addProjectToSession = (session, projectId) => {
	if (!sessionStore.has(session.id)) throw new Exception("Bad Request", "Invalid session", 400);
	sessionStore.set(session.id, {
		...session,
		projects: Array.from(new Set([...session.projects, projectId]).values())
	});
};
var getOrCreateSession = async (sessionHandler) => {
	const session = await resolveSession(sessionHandler);
	if (session) return session;
	const id = generateKey(10);
	sessionStore.set(id, {
		id,
		projects: []
	});
	await sessionHandler.setData({ id });
	await sessionHandler.save();
	return sessionStore.get(id);
};
var sessionHandle = sveltekitSessionHandle({ secret: private_env.SESSION_SECRET || generateKey() });
var checkAuthorizationHandle = async ({ event, resolve }) => {
	const sessionData = event.locals.session.data;
	if (isPublicPath(event.url)) return resolve(event);
	if (!isSessionExist(sessionData.id)) {
		await resetSession(event.locals.session);
		throw redirect(302, "/");
	}
	return resolve(event);
};
var projectStore = /* @__PURE__ */ new Map();
var projectExists = (id) => {
	return !!id && projectStore.has(id);
};
var hasRightToAccessProject = (id, session) => {
	return projectExists(id) && session.projects.includes(id);
};
var resolveProject = (headers, session) => {
	if (!session) return null;
	if (!session.projects.length) return null;
	return getProject(headers.get(SESSION_PROJECT_HEADER), session);
};
var getProject = (id, session) => {
	if (!hasRightToAccessProject(id, session)) return null;
	return projectStore.get(id) ?? null;
};
var tryAddProjectSession = (session) => {
	let id = generateKey(10);
	while (projectStore.has(id)) id = generateKey(10);
	projectStore.set(id, session);
	return id;
};
//#endregion
export { getProject as a, sessionHandle as c, getOrCreateSession as i, tryAddProjectSession as l, checkAuthorizationHandle as n, resolveProject as o, generateKey as r, resolveSession as s, addProjectToSession as t };

//# sourceMappingURL=session-DLCjVGKo.js.map