import type { SecretService, } from '@pikku/core' import type { KyselyDB } from './database-types.js' import { JsonConsoleLogger, LocalSecretService, LocalVariablesService, } from '@pikku/core/services' import { CFWorkerSchemaService } from '@pikku/schema-cfworker' import { LibsqlWebDialect } from '@pikku/kysely-sqlite' import { Kysely, CamelCasePlugin } from 'kysely' import './middleware.js' import { ProjectEmailService } from './services/email-service.js' import { MailgunEmailService } from './services/mailgun/mailgun.js' import { RecognitionDossierService } from './services/recognition-dossier-service.js' import { TranslationService } from './services/translation-service.js' import { pikkuServices, pikkuWireServices, } from '#pikku/function/pikku-function-types.gen.js' export const createSingletonServices = pikkuServices(async (config, existingServices) => { const variables = existingServices?.variables ?? new LocalVariablesService() const logger = existingServices?.logger ?? new JsonConsoleLogger() const secrets: SecretService = existingServices?.secrets ?? new LocalSecretService(variables) if (config.logLevel) { logger.setLevel(config.logLevel) } const schema = existingServices?.schema ?? new CFWorkerSchemaService(logger) // On CF Workers Fabric injects DATABASE_URL as a variable but does NOT inject a // kysely service (the SQL dialect is project-specific), so construct our own — // mirroring the green germantax app. pikku-dev / sandbox still inject kysely. let kysely = existingServices?.kysely as Kysely | undefined if (!kysely) { const databaseUrl = await variables.get('DATABASE_URL') if (!databaseUrl) { throw new Error( 'DATABASE_URL not set — in dev enable `dev.db` in the pikku config; in prod Fabric injects it', ) } kysely = new Kysely({ dialect: new LibsqlWebDialect({ url: databaseUrl }), plugins: [new CamelCasePlugin()], }) } if (!existingServices?.content) { throw new Error('content service was not injected by the runtime (pikku dev / fabric)') } const content = existingServices.content const emailDelegate = existingServices?.emailService ?? new MailgunEmailService( variables.get('NODE_ENV') === 'production' ? await secrets.getSecret(config.secrets.mailgunApiKey) : undefined, config.domain ) const emailService = new ProjectEmailService(emailDelegate) const recognitionDossierService = existingServices?.recognitionDossierService ?? new RecognitionDossierService(kysely, content, logger) const translation = existingServices?.translation ?? new TranslationService(kysely) if (!existingServices?.translation) { await translation.init() } return { ...(existingServices ?? {}), config, variables, secrets, schema, logger, kysely, content, emailService, recognitionDossierService, translation, } }) export const createWireServices = pikkuWireServices(async () => ({ }))