import { JsonConsoleLogger, LocalSecretService, LocalVariablesService, } from '@pikku/core/services' import { pikkuServices } from '../.pikku/pikku-types.gen.js' import { CFWorkerSchemaService } from '@pikku/schema-cfworker' import { JoseJWTService } from '@pikku/jose' import { LibsqlWebDialect } from '@pikku/kysely-sqlite' import { Kysely } from 'kysely' import type { DB } from './types/db.types.js' import { EmailService } from './services/email.js' // Dev fallback only — in prod JWT_SECRET must be set (magic-link tokens are // signed with it). A rotated-out value can be appended as a second secret. const DEV_JWT_SECRET = 'dev-insecure-jwt-secret-change-me' export const createSingletonServices = pikkuServices(async ( config, existingServices, ) => { const variables = existingServices?.variables ?? new LocalVariablesService() const secrets = existingServices?.secrets ?? new LocalSecretService(variables) const logger = existingServices?.logger ?? new JsonConsoleLogger() const schema = existingServices?.schema ?? new CFWorkerSchemaService(logger) let kysely = existingServices?.kysely if (!kysely) { const databaseUrl = await variables.get('DATABASE_URL') if (!databaseUrl) { throw new Error( 'DATABASE_URL not set — in dev, enable `dev.db` in your pikku config; in prod, Fabric injects DATABASE_URL', ) } kysely = new Kysely({ dialect: new LibsqlWebDialect({ url: databaseUrl }) }) } const email = existingServices?.email ?? new EmailService(logger) const content = existingServices?.content const jwt = existingServices?.jwt ?? new JoseJWTService(async () => { const secret = (await variables.get('JWT_SECRET')) ?? DEV_JWT_SECRET return [{ id: 'k1', value: secret }] }, logger) return { ...(existingServices ?? {}), config, variables, secrets, logger, schema, kysely, email, content, jwt, } })