/** * This Source Code is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright (c) Infonomic Company Limited */ /** * Server-side Byline bootstrap. Imported as a side-effect from * `src/server.ts` and from scripts that need the configured runtime. * * This is the minimal scaffold: no example collections or search provider. */ import { type AdminStore, registerAdminAbilities } from '@byline/admin' import { JwtSessionProvider } from '@byline/admin/auth' import { getAdminBylineClient } from '@byline/client/server' import { type BylineCore, initBylineCore } from '@byline/core' import { mysqlAdapter } from '@byline/db-mysql' import { createAdminStore } from '@byline/db-mysql/admin' import { registerTanstackStartHostBridge } from '@byline/host-tanstack-start/integrations/host-bridge' import { lexicalEditorEmbedServer, lexicalEditorPopulateServer, lexicalEditorToMarkdownServer, } from '@byline/richtext-lexical/server' import { localStorageProvider } from '@byline/storage-local' import { collections } from './collections/index.js' import { serverHooks } from './collections/server-hooks.js' import { i18n } from './i18n.js' import { routes } from './routes.js' // HMR-safe singleton. Reusing the resolving Promise prevents development // reloads from orphaning MySQL pools and exhausting server connections. declare global { // biome-ignore lint: globalThis augmentation requires `var` rather than `let` var __bylineCoreSingleton__: Promise> | undefined } async function buildBylineCore(): Promise> { const db = mysqlAdapter({ connectionString: process.env.BYLINE_DB_MYSQL_CONNECTION_STRING || '', collections, defaultContentLocale: i18n.content.defaultLocale, // Optional pool tuning. Omit these values to use the adapter defaults: // 20 connections, 2s idle timeout, and 30s connection timeout. connectionLimit: process.env.BYLINE_DB_MYSQL_CONNECTION_LIMIT ? Number(process.env.BYLINE_DB_MYSQL_CONNECTION_LIMIT) : undefined, idleTimeout: process.env.BYLINE_DB_MYSQL_IDLE_TIMEOUT_MILLIS ? Number(process.env.BYLINE_DB_MYSQL_IDLE_TIMEOUT_MILLIS) : undefined, connectTimeout: process.env.BYLINE_DB_MYSQL_CONNECTION_TIMEOUT_MILLIS ? Number(process.env.BYLINE_DB_MYSQL_CONNECTION_TIMEOUT_MILLIS) : undefined, }) const adminStore = createAdminStore(db.drizzle) const signingSecret = process.env.BYLINE_JWT_SECRET if (!signingSecret || signingSecret.length < 32) { throw new Error( 'BYLINE_JWT_SECRET must be set and carry at least 32 bytes of entropy. ' + 'Generate one with `openssl rand -base64 48` and add it to your .env.local.' ) } const sessionProvider = new JwtSessionProvider({ store: adminStore, signingSecret, }) registerTanstackStartHostBridge() const core = await initBylineCore({ i18n, routes, collections, hooks: serverHooks, db, adminStore, storage: localStorageProvider({ uploadDir: './uploads', baseUrl: '/uploads', }), sessionProvider, fields: { richText: { embed: lexicalEditorEmbedServer({ getClient: getAdminBylineClient }), populate: lexicalEditorPopulateServer({ getClient: getAdminBylineClient }), toMarkdown: lexicalEditorToMarkdownServer(), }, }, }) registerAdminAbilities(core.abilities) return core } globalThis.__bylineCoreSingleton__ ??= buildBylineCore() await globalThis.__bylineCoreSingleton__