appos 0.3.1-0 → 0.3.2-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/main.mjs +1 -1
- package/dist/exports/api/workflows/extract-blob-metadata.mjs +1 -1
- package/dist/exports/api/workflows/generate-image-variant.d.mts +2 -2
- package/dist/exports/api/workflows/generate-image-variant.mjs +1 -1
- package/dist/exports/api/workflows/generate-preview.mjs +1 -1
- package/dist/exports/api/workflows/purge-attachment.mjs +1 -1
- package/dist/exports/api/workflows/purge-audit-logs.mjs +1 -1
- package/dist/exports/api/workflows/purge-unattached-blobs.mjs +1 -1
- package/dist/exports/api/workflows/track-db-changes.mjs +1 -1
- package/package.json +1 -1
package/dist/bin/main.mjs
CHANGED
|
@@ -335,4 +335,4 @@ export default async function seed(container: AppOS.Container) {}
|
|
|
335
335
|
`;async function exists(filePath){try{return await fs$1.access(filePath),!0}catch{return!1}}async function findLatestSnapshot(migrationsFolder){if(!await exists(migrationsFolder))return null;let migrationDirs=(await fs$1.readdir(migrationsFolder,{withFileTypes:!0})).filter(e=>e.isDirectory()&&/^\d{14}_/.test(e.name)).map(e=>e.name).sort().reverse();if(migrationDirs.length===0)return null;let snapshotPath=path.join(migrationsFolder,migrationDirs[0],`snapshot.json`);if(!await exists(snapshotPath))return null;let content=await fs$1.readFile(snapshotPath,`utf-8`);return JSON.parse(content)}async function ensureDatabaseScaffolding(dbName){let dbFolder=path.join(process.cwd(),APPOS_DIR,DATABASES_DIR,dbName);await exists(dbFolder)||await fs$1.mkdir(dbFolder,{recursive:!0});let schemaPath=path.join(dbFolder,`schema.ts`);await exists(schemaPath)||await fs$1.writeFile(schemaPath,SCHEMA_TEMPLATE(dbName));let relationsPath=path.join(dbFolder,`relations.ts`);await exists(relationsPath)||await fs$1.writeFile(relationsPath,RELATIONS_TEMPLATE(dbName));let seedPath=path.join(dbFolder,`seed.ts`);await exists(seedPath)||await fs$1.writeFile(seedPath,SEED_TEMPLATE(dbName));let schemaMigrationsFolder=path.join(dbFolder,`schema-migrations`);await exists(schemaMigrationsFolder)||await fs$1.mkdir(schemaMigrationsFolder,{recursive:!0});let dataMigrationsFolder=path.join(dbFolder,`data-migrations`);await exists(dataMigrationsFolder)||await fs$1.mkdir(dataMigrationsFolder,{recursive:!0})}async function generateDataMigrations(opts){let{ctx}=opts,{container}=ctx,databaseNames=Object.keys(container.db);if(ctx.info(`Generating data migration`),databaseNames.length===0){ctx.info(`No databases available`);return}let selectedDb=await ctx.choice({message:`Select database`,options:databaseNames.map(name=>({value:name,label:name}))});ctx.isCancel(selectedDb)&&ctx.cancel();let dbName=selectedDb;await ensureDatabaseScaffolding(dbName);let{migrationsFolder}=defineMigrationOpts(dbName,`data`),migrationFile=await ctx.task(dbName,async()=>{let migrationTag=`${new Date().toISOString().replace(/[-:T]/g,``).slice(0,14)}_${generateMigrationName()}`,migrationFolder=path.join(migrationsFolder,migrationTag);await fs$1.mkdir(migrationFolder,{recursive:!0});let file=path.join(migrationFolder,`migration.sql`);return await fs$1.writeFile(file,`-- Write your data migration SQL here
|
|
336
336
|
`),file});ctx.success(migrationFile)}async function generateMigrations(opts){let{ctx,migrationType}=opts,{container}=ctx;if(migrationType===`data`){await generateDataMigrations(opts);return}let databaseNames=Object.keys(container.db);if(ctx.info(`Generating ${migrationType} migrations`),databaseNames.length===0){ctx.info(`Nothing to generate`);return}for(let name of databaseNames)await ensureDatabaseScaffolding(name);let createdAny=!1;for(let name of databaseNames){let schemaPath=path.join(process.cwd(),APPOS_DIR,DATABASES_DIR,name,`schema.ts`),{migrationsFolder}=defineMigrationOpts(name,migrationType);try{let schemaModule=await import(schemaPath),schema=schemaModule.default||schemaModule,prevJson=await findLatestSnapshot(migrationsFolder)??await generateDrizzleJson({}),currentJson=await generateDrizzleJson(schema),migrationStatements=await generateMigration(prevJson,currentJson);if(!migrationStatements||migrationStatements.length===0){ctx.taskStatus(name,`no changes`);continue}let migrationFile=await ctx.task(name,async()=>{let migrationTag=`${new Date().toISOString().replace(/[-:T]/g,``).slice(0,14)}_${generateMigrationName()}`,migrationFolder=path.join(migrationsFolder,migrationTag);await fs$1.mkdir(migrationFolder,{recursive:!0});let sqlContent=`${migrationStatements.join(`;
|
|
337
337
|
|
|
338
|
-
`)};`,file=path.join(migrationFolder,`migration.sql`),snapshotFile=path.join(migrationFolder,`snapshot.json`);return await fs$1.writeFile(file,sqlContent),await fs$1.writeFile(snapshotFile,JSON.stringify(currentJson,null,2)),file});ctx.success(migrationFile),createdAny=!0}catch(error){ctx.fail(error instanceof Error?error.message:String(error))}}createdAny||ctx.info(`Nothing to generate`)}async function runMigrations(opts){let{ctx,migrationType}=opts,{container}=ctx,databaseNames=Object.keys(container.db),originalLogLevel=container.logger.level;container.logger.level=`silent`,migrationType===`schema`&&await(0,import_system_database.ensureSystemDatabase)(container.worker.dbUrl,container.logger);let hasAnyMigrations=(await Promise.all(databaseNames.map(async name=>{let{migrationsFolder}=defineMigrationOpts(name,migrationType);return exists(migrationsFolder)}))).some(Boolean);if(databaseNames.length===0||!hasAnyMigrations){ctx.info(`Nothing to migrate`),container.logger.level=originalLogLevel;return}for(let name of databaseNames){let db=container.db[name],migrationOpts=defineMigrationOpts(name,migrationType);if(await exists(migrationOpts.migrationsFolder))try{await ctx.task(name,async()=>{await migrate(db,migrationOpts)})}catch(error){ctx.fail(error instanceof Error?error.message:String(error))}}container.logger.level=originalLogLevel}async function runSeeds(opts){let{ctx}=opts,{container}=ctx,databaseNames=Object.keys(container.db);if(databaseNames.length===0){ctx.info(`Nothing to seed`);return}for(let name of databaseNames)await ensureDatabaseScaffolding(name);for(let name of databaseNames){let seedPath=path.join(process.cwd(),APPOS_DIR,DATABASES_DIR,name,`seed.ts`);try{await ctx.task(name,async()=>{let seedModule=await import(seedPath),seedFn=seedModule.default||seedModule.seed;if(typeof seedFn!=`function`)throw Error(`seed file does not export a function`);await seedFn(container)})}catch(error){ctx.fail(error instanceof Error?error.message:String(error))}}}var gen_default$1=defineCommand({description:`Generate database data migrations`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`db:data:gen`),await generateMigrations({ctx,migrationType:`data`}),await ctx.cleanup(),ctx.outro()}}),migrate_default$1=defineCommand({description:`Run pending database data migrations`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`db:data:migrate`),await runMigrations({ctx,migrationType:`data`}),await ctx.cleanup(),ctx.outro()}}),gen_default=defineCommand({description:`Generate database schema migrations from schema changes`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`db:schema:gen`),await generateMigrations({ctx,migrationType:`schema`}),await ctx.cleanup(),ctx.outro()}}),migrate_default=defineCommand({description:`Run pending database schema migrations`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`db:schema:migrate`),await runMigrations({ctx,migrationType:`schema`}),await ctx.cleanup(),ctx.outro()}}),seed_default=defineCommand({description:`Seed databases with initial data`,args:z.tuple([]),opts:z.object({}),async run(ctx){await runSeeds({ctx}),await ctx.cleanup()}}),down_default=defineCommand({description:`Teardown the infrastructure`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`down`);try{await ctx.spinner(`Stopping containers`,()=>ctx.exec(`docker`,[`compose`,`--profile=infra`,`down`,`--remove-orphans`,`--timeout=0`]))}catch(error){ctx.fail(error instanceof Error?error.message:String(error))}ctx.outro()}}),preview_default=defineCommand({description:`Preview the application`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`preview`),ctx.step(`Starting containers (Ctrl+C to stop)`);let proc=spawn(`docker`,[`compose`,`--profile=app`,`up`,`--build`],{stdio:`inherit`,cwd:process.cwd()}),isShuttingDown=!1,cleanup=async()=>{if(!isShuttingDown){isShuttingDown=!0,proc.kill(`SIGTERM`),ctx.step(`Stopping containers`);try{await ctx.exec(`docker`,[`compose`,`--profile=app`,`down`,`--remove-orphans`,`--timeout=0`])}catch{}process.exit(0)}};process.on(`SIGINT`,cleanup),process.on(`SIGTERM`,cleanup),proc.on(`close`,()=>{isShuttingDown||cleanup()}),await new Promise(()=>{})}}),version=`0.3.1-0`;const runtimeInfo$1=`(AppOS v${version} | Node ${process.version})`;var repl_default=defineCommand({description:`Start an interactive REPL with container access`,aliases:[`r`],args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`repl`);let{container}=ctx,available=[],dbNames=Object.keys(container.db);dbNames.length>0&&available.push(`container.db.{${dbNames.join(`, `)}}`);let cacheNames=Object.keys(container.cache);cacheNames.length>0&&available.push(`container.cache.{${cacheNames.join(`, `)}}`);let storageNames=Object.keys(container.storage);storageNames.length>0&&available.push(`container.storage.{${storageNames.join(`, `)}}`);let otherKeys=Object.keys(container).filter(k=>k!==`db`&&k!==`cache`&&k!==`storage`).map(k=>`container.${k}`);available.push(...otherKeys),ctx.info(`Starting REPL ${runtimeInfo$1}`),ctx.info(`Available: ${available.join(`, `)}`),ctx.line(``);let replServer=repl.start({prompt:`appos(${container.config.APP_NAME})> `,useColors:!0,useGlobal:!0,breakEvalOnSigint:!0});replServer.context.container=container,replServer.on(`exit`,async()=>{await ctx.cleanup(),ctx.info(`Bye`),process.exit(0)}),await new Promise(()=>{})}}),reset_default=defineCommand({description:`Reset the infrastructure`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`reset`);try{await ctx.spinner(`Stopping containers`,()=>ctx.exec(`docker`,[`compose`,`--profile=infra`,`down`,`--remove-orphans`,`--timeout=0`]))}catch(error){ctx.fail(error instanceof Error?error.message:String(error))}try{await ctx.spinner(`Starting containers`,()=>ctx.exec(`docker`,[`compose`,`--profile=infra`,`up`,`--wait`]))}catch(error){ctx.fail(error instanceof Error?error.message:String(error))}ctx.step(`Running schema migrations`),await runMigrations({ctx,migrationType:`schema`}),ctx.step(`Seeding databases`),await runSeeds({ctx}),await ctx.cleanup(),ctx.outro()}});function getErrorStatus(error){if(error&&typeof error==`object`&&`status`in error){let status=error.status;if(typeof status==`number`)return status}return error instanceof SyntaxError?400:500}function defineErrorHandlerMiddleware(logger){return(error,req,res,next)=>{let userAgent=req.headers[`user-agent`];if(logger.error({error:error.message,stack:error.stack,url:req.url,method:req.method,userAgent:typeof userAgent==`string`?userAgent:userAgent?.[0],ip:req.ip},`Unhandled request error`),res.headersSent)return next(error);let isDevelopment=process.env.NODE_ENV!==`production`;res.status(getErrorStatus(error)).json({error:`Internal server error`,...isDevelopment&&{message:error.message,stack:error.stack}})}}function defineHealthMiddleware(path$2=`/health`){return(req,res,next)=>{if(req.path!==path$2)return next();res.status(200).json({status:`healthy`,timestamp:new Date().toISOString(),uptime:process.uptime()})}}function defineI18nMiddleware(i18n){return(req,res,next)=>{let acceptLanguage=req.headers[`accept-language`],detectedLng=`en`;if(acceptLanguage){let parsed=acceptLanguage.split(`,`)[0].split(`;`)[0].trim();parsed&&parsed.length>0&&(detectedLng=parsed)}req.dir=i18n.dir(detectedLng),req.language=detectedLng,req.languages=i18n.languages,req.t=i18n.getFixedT(detectedLng),next()}}var instrumentation_exports={};import*as import__opentelemetry_api from"@opentelemetry/api";__reExport(instrumentation_exports,import__opentelemetry_api),register(`@opentelemetry/instrumentation/hook.mjs`,import.meta.url);const service={name:process.env.APP_NAME||`appos`,version:process.env.APP_VERSION||`development`};new NodeSDK({resource:resourceFromAttributes({[ATTR_SERVICE_NAME]:service.name,[ATTR_SERVICE_VERSION]:service.version}),spanProcessors:[new BatchSpanProcessor(new OTLPTraceExporter({url:process.env.OTEL_EXPORTER_OTLP_ENDPOINT||`http://localhost:4318/v1/traces`,headers:process.env.OTEL_EXPORTER_OTLP_HEADERS?JSON.parse(process.env.OTEL_EXPORTER_OTLP_HEADERS):void 0}))],instrumentations:[getNodeAutoInstrumentations({"@opentelemetry/instrumentation-fs":{enabled:!1},"@opentelemetry/instrumentation-dns":{enabled:!1}}),new PinoInstrumentation({logHook:(_span,record)=>{record[`service.name`]=service.name}})]}).start();const CF_COUNTRY_HEADER=`cf-ipcountry`;function extractRequestMetadata(req,config){config?.injectCountry&&!req.headers[CF_COUNTRY_HEADER]&&(req.headers[CF_COUNTRY_HEADER]=config.injectCountry);let forwardedFor=req.headers[`x-forwarded-for`],ipAddress;ipAddress=typeof forwardedFor==`string`?forwardedFor.split(`,`)[0].trim():Array.isArray(forwardedFor)&&forwardedFor.length>0?forwardedFor[0].split(`,`)[0].trim():req.socket.remoteAddress||``;let userAgent=req.headers[`user-agent`]||``,country=req.headers[CF_COUNTRY_HEADER]||``;return{ipAddress,userAgent,country}}function defineRequestLoggerMiddleware(logger,config){let tracer;try{tracer=instrumentation_exports.trace.getTracer(`request-logger`)}catch(error){logger.warn({error},`Failed to initialize OpenTelemetry tracer`),tracer=null}return(req,res,next)=>{let metadata=extractRequestMetadata(req,config);req.metadata=metadata;let startTime=Date.now(),span=null,requestId;if(tracer)try{span=tracer.startSpan(`${req.method} ${req.path}`,{kind:instrumentation_exports.SpanKind.SERVER,attributes:{"http.method":req.method,"http.url":req.url,"http.target":req.path,"http.host":(typeof req.headers.host==`string`?req.headers.host:req.headers[`:authority`])||`unknown`,"http.scheme":req.protocol,"http.user_agent":metadata.userAgent||`unknown`,"http.client_ip":metadata.ipAddress,"http.client_country":metadata.country}}),requestId=span.spanContext().traceId}catch(error){logger.warn({error},`Failed to create OpenTelemetry span`),span=null,requestId=crypto.randomUUID()}else logger.info(`OpenTelemetry tracer not available, skipping span creation`),span=null,requestId=crypto.randomUUID();let ctx=span?instrumentation_exports.trace.setSpan(instrumentation_exports.context.active(),span):instrumentation_exports.context.active(),skipLogging=(path$2=>path$2.startsWith(`/@`)||path$2.startsWith(`/node_modules/`)||path$2.startsWith(`/assets/`)||path$2.startsWith(`/public/`)||path$2.match(/\.(js|css|ico|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|map)$/)||path$2.includes(`/locales/`)||path$2.endsWith(`.ts`)||path$2.endsWith(`.tsx`)||path$2.endsWith(`.json`))(req.path);res.setHeader(`X-Request-Id`,requestId),req.requestId=requestId,res.on(`finish`,()=>{let duration=Date.now()-startTime;if(span)try{span.setAttributes({"http.status_code":res.statusCode,"http.response_content_length":res.get(`content-length`)||0}),res.statusCode>=400?span.setStatus({code:instrumentation_exports.SpanStatusCode.ERROR,message:`HTTP ${res.statusCode}`}):span.setStatus({code:instrumentation_exports.SpanStatusCode.OK}),span.end()}catch(error){logger.warn({error},`Failed to update OpenTelemetry span`)}skipLogging||instrumentation_exports.context.with(ctx,()=>{logger.info({requestId,method:req.method,url:req.path,status:res.statusCode,duration:`${duration}ms`},`Request completed`)})}),res.on(`close`,()=>{if(!(res.writableEnded||res.finished)){let duration=Date.now()-startTime;if(span)try{span.setStatus({code:instrumentation_exports.SpanStatusCode.ERROR,message:`Request closed unexpectedly`}),span.end()}catch(error){logger.warn({error},`Failed to update OpenTelemetry span`)}skipLogging||instrumentation_exports.context.with(ctx,()=>{logger.warn({requestId,method:req.method,url:req.path,duration:`${duration}ms`},`Request closed unexpectedly`)})}});let originalNext=next;next=error=>{if(error){let duration=Date.now()-startTime;if(span)try{span.recordException(error),span.setStatus({code:instrumentation_exports.SpanStatusCode.ERROR,message:error instanceof Error?error.message:String(error)}),span.end()}catch(spanError){logger.warn({error:spanError},`Failed to record exception in span`)}skipLogging||instrumentation_exports.context.with(ctx,()=>{logger.error({requestId,method:req.method,url:req.path,status:res.statusCode||500,duration:`${duration}ms`,error:error instanceof Error?error.message:String(error)},`Request failed`)})}originalNext(error)},instrumentation_exports.context.with(ctx,()=>next())}}function defineShutdownMiddleware(logger,isShuttingDown){return(req,res,next)=>{if(isShuttingDown()){logger.info({url:req.url,method:req.method},`Rejecting request during shutdown`),res.status(503).set(`Connection`,`close`).json({error:`Server is shutting down`});return}next()}}function defineTimeoutMiddleware(logger,timeoutMs=3e4){return(req,res,next)=>{let timeoutFired=!1,timeoutId=setTimeout(()=>{timeoutFired=!0,logger.warn({url:req.url,method:req.method,timeout:timeoutMs},`Request timeout`),res.headersSent||res.status(408).json({error:`Request timeout`}),res.removeListener(`finish`,clearTimeoutHandler),res.removeListener(`close`,clearTimeoutHandler)},timeoutMs),clearTimeoutHandler=()=>{timeoutFired||clearTimeout(timeoutId)};res.on(`finish`,clearTimeoutHandler),res.on(`close`,clearTimeoutHandler),next()}}function getMiddlewareName(filename){return camelCase(basename(filename,`.ts`).replace(/^\d+_/,``))}async function loadMiddleware(middlewareDir,container,app){try{await fs$1.access(middlewareDir)}catch{return}let files=[];for await(let file of glob(`${middlewareDir}/**/*.${FILE_EXT}`))!file.endsWith(`.test.ts`)&&!file.endsWith(`.spec.ts`)&&!file.endsWith(`.test.js`)&&!file.endsWith(`.spec.js`)&&files.push(file);files.sort((a,b)=>basename(a).localeCompare(basename(b)));for(let file of files)try{let middleware=(await import(file)).default;if(middleware&&typeof middleware.handler==`function`){let name=middleware.name??getMiddlewareName(file);container.logger.debug({name,file},`Loading user middleware`),app.use(middleware.handler(container))}else container.logger.warn({file},`Middleware file missing default export with handler function`)}catch(err){throw container.logger.error({file,error:err instanceof Error?err.message:err},`Failed to load middleware`),err}}const outputDir=resolve(PUBLIC_DIR,`openapi`);function extractVersion(path$2){return path$2.match(/^\/(v\d+)/)?.[1]}function convertToOpenAPIPath(reactRouterPath){return reactRouterPath.replace(/:(\w+)/g,`{$1}`)}function buildPathObject(path$2,spec){let openApiPath=convertToOpenAPIPath(path$2),methodConfig={summary:spec.summary,description:spec.description};(spec.request?.params||spec.request?.query||spec.request?.headers)&&(methodConfig.requestParams={},spec.request.params&&(methodConfig.requestParams.path=spec.request.params),spec.request.query&&(methodConfig.requestParams.query=spec.request.query),spec.request.headers&&(methodConfig.requestParams.header=spec.request.headers)),spec.request?.body&&(methodConfig.requestBody={content:{"application/json":{schema:spec.request.body}}}),methodConfig.responses={};for(let[statusCode,response]of Object.entries(spec.responses))methodConfig.responses[statusCode]={description:response.description,content:{"application/json":{schema:response.content[`application/json`].schema}}};return{path:openApiPath,config:methodConfig}}function generateOpenAPIDocument(routes,version$1,config){let paths={};for(let route of routes){if(route.version!==version$1)continue;let apiPath=route.path.replace(`/${version$1}`,``)||`/`;for(let spec of route.openAPISpec){let{path:openApiPath,config:methodConfig}=buildPathObject(apiPath,spec);paths[openApiPath]||(paths[openApiPath]={}),paths[openApiPath][spec.method]=methodConfig}}return createDocument({openapi:`3.1.0`,info:config?.info||{title:`API ${version$1.toUpperCase()}`,version:version$1.replace(`v`,``),description:`OpenAPI specification for ${version$1} API`},servers:config?.servers||[{url:`http://localhost:8000/${version$1}`,description:process.env.NODE_ENV===`production`?`Production server`:`Development server`}],paths})}async function scanAPIRoutes(container){let fullPath=resolve(APPOS_DIR,ROUTES_DIR);try{await access(fullPath)}catch(error){return container.logger.error({error},`OpenAPI routes directory not found`),[]}let routeManifest=await remixRoutesOptionAdapter(defineRoutes=>flatRoutes(ROUTES_DIR,defineRoutes,{appDir:APPOS_DIR,ignoredRouteFiles:[`**/.*`,`**/*.{spec,test}.{ts,tsx}`,`**/*-????????.{js,ts}`]})),routes=[];for(let[routeId,route]of Object.entries(routeManifest)){let absolutePath=resolve(APPOS_DIR,route.file).replace(/.ts$/,process.env.NODE_ENV===`production`?`.js`:`.ts`);try{let exported=(await import(absolutePath)).default;if(!exported||isEmpty(exported)||/\/openapi.(j|t)s$/.test(absolutePath))continue;if(!exported?.handlers&&!exported?.openAPISpec){container.logger.warn(`Missing default export with 'defineOpenAPI' for '${APPOS_DIR}/${route.file}'`);continue}let routePath=`/${route.path}`;routes.push({path:routePath,filePath:absolutePath,handlers:exported.handlers,openAPISpec:exported.openAPISpec,version:extractVersion(routePath)})}catch(error){container.logger.error(error,`Error loading route ${route.file}:`)}}return routes}function registerRoutes(app,routes){for(let route of routes)for(let[method,handler]of Object.entries(route.handlers))app[method.toLowerCase()](route.path,handler)}async function loadVersionConfig(container,version$1){let configPath=resolve(APPOS_DIR,ROUTES_DIR,`${version$1}+/openapi.${FILE_EXT}`);try{let configFn=(await import(configPath)).default;return configFn?configFn(container):void 0}catch{return}}async function writeOpenAPISpecs(container,routes){let versions=[...new Set(routes.map(r=>r.version).filter(Boolean))];versions.length>0&&await mkdir(outputDir,{recursive:!0});for(let version$1 of versions){let doc=generateOpenAPIDocument(routes,version$1,await loadVersionConfig(container,version$1));await writeFile(join(outputDir,`${version$1}.json`),JSON.stringify(doc,null,2),`utf-8`)}}async function loadAndRegisterAPIRoutes(app){let routes=await scanAPIRoutes(app.locals.container);registerRoutes(app,routes),process.env.NODE_ENV!==`production`&&await writeOpenAPISpecs(app.locals.container,routes)}async function defineServer({container}){let isShuttingDown=!1,{host=`0.0.0.0`,port,timeout=3e4,bodyLimit=`1mb`,healthPath=`/health`,cors:corsConfig,helmet:helmetConfig,rateLimit:rateLimitConfigs,redisUrl}=container.server,app=express();app.locals.container=container;let redisClient=null;if(redisUrl&&rateLimitConfigs)try{redisClient=createClient({url:redisUrl}),redisClient.on(`error`,err=>{container.logger.error({error:err.message},`Redis client error`)}),await redisClient.connect(),container.logger.info(`Connected to Redis for rate limiting`)}catch(err){container.logger.error({error:err instanceof Error?err.message:err},`Failed to connect to Redis, falling back to in-memory rate limiting`),redisClient=null}if(app.use(defineShutdownMiddleware(container.logger,()=>isShuttingDown),defineTimeoutMiddleware(container.logger,timeout),defineRequestLoggerMiddleware(container.logger),defineHealthMiddleware(healthPath)),helmetConfig!==void 0&&app.use(helmet(helmetConfig)),corsConfig!==void 0&&app.use(cors(corsConfig)),rateLimitConfigs)for(let[index,config]of rateLimitConfigs.entries()){let limiter=rateLimit({windowMs:config.windowMs??60*1e3,limit:config.limit??100,standardHeaders:config.standardHeaders??`draft-8`,legacyHeaders:config.legacyHeaders??!1,skip:config.skip,keyGenerator:config.keyGenerator,handler:config.handler,message:config.message,statusCode:config.statusCode,requestPropertyName:config.requestPropertyName,skipFailedRequests:config.skipFailedRequests,skipSuccessfulRequests:config.skipSuccessfulRequests,requestWasSuccessful:config.requestWasSuccessful,validate:config.validate,store:redisClient?new RedisStore({sendCommand:(...args)=>redisClient.sendCommand(args),prefix:`rl:${index}:`}):config.store});app.use(limiter)}if(app.use(defineI18nMiddleware(container.i18n),express.json({limit:bodyLimit}),express.urlencoded({extended:!0,limit:bodyLimit})),await loadMiddleware(join(process.cwd(),`api`,`middleware`),container,app),app.get(`/`,(_req,res)=>{res.json({message:`${container.config.APP_NAME} server is running.`})}).all(`${container.auth.options.basePath}/*`,toNodeHandler(container.auth)),await loadAndRegisterAPIRoutes(app),app.use(express.static(resolve(PUBLIC_DIR))),process.env.NODE_ENV!==`production`){let{defineYouchErrorHandler}=await import(`./youch-handler-BadUgHb0.mjs`);app.use(defineYouchErrorHandler(container.logger))}else app.use(defineErrorHandlerMiddleware(container.logger));return{app,host,port,async start(){await new Promise((resolve$1,reject)=>{try{app.listen(port,host,()=>{resolve$1()})}catch(err){reject(err)}})},async close(){if(isShuttingDown=!0,app.uwsApp.close(),redisClient)try{await redisClient.destroy()}catch(err){container.logger.warn({error:err instanceof Error?err.message:err},`Error disconnecting from Redis`)}}}}function defineWorker(){return{async start(){await DBOS.launch()},async close(){await DBOS.shutdown()}}}const runtimeInfo=`(AppOS v${version} | Node ${process.version})`;var start_default=defineCommand({description:`Start the HTTP server and/or DBOS workflow worker`,aliases:[`s`],args:z.tuple([]),opts:z.object({service:z.enum([`server`,`worker`]).optional().meta({short:`s`,description:`Start only a specific service (default: both)`})}),async run(ctx){let{container,opts}=ctx,{logger}=container,startServer=!opts.service||opts.service===`server`,startWorker=!opts.service||opts.service===`worker`,server=null,worker=null,isShuttingDown=!1,shutdown=async signal=>{if(!isShuttingDown){isShuttingDown=!0,logger.info(`Shutdown initiated (${signal})`);try{server&&(await server.close(),logger.info(`HTTP server stopped`)),worker&&(await worker.close(),logger.info(`DBOS worker stopped`)),await ctx.cleanup()}catch(error){logger.error({error},`Shutdown error`)}process.exit(0)}};process.on(`SIGINT`,()=>shutdown(`SIGINT`)),process.on(`SIGTERM`,()=>shutdown(`SIGTERM`)),startWorker&&(worker=defineWorker(),await worker.start(),logger.info(`DBOS worker ready ${runtimeInfo}`)),startServer&&(server=await defineServer({container}),await server.start(),logger.info(`HTTP server ready at http://${server.host}:${server.port} ${runtimeInfo}`))}}),up_default=defineCommand({description:`Setup the infrastructure`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`up`);try{await ctx.spinner(`Starting containers`,()=>ctx.exec(`docker`,[`compose`,`--profile=infra`,`up`,`--wait`]))}catch(error){ctx.fail(error instanceof Error?error.message:String(error))}ctx.step(`Running schema migrations`),await runMigrations({ctx,migrationType:`schema`}),ctx.step(`Seeding databases`),await runSeeds({ctx}),await ctx.cleanup(),ctx.outro()}});const BUILTIN_COMMANDS={build:build_default,"db:data:gen":gen_default$1,"db:data:migrate":migrate_default$1,"db:schema:gen":gen_default,"db:schema:migrate":migrate_default,"db:seed":seed_default,down:down_default,preview:preview_default,repl:repl_default,reset:reset_default,start:start_default,up:up_default};function scanCommands(dir,baseDir=dir){let files=[];try{let entries=fs.readdirSync(dir,{withFileTypes:!0});for(let entry of entries){let fullPath=path.join(dir,entry.name);entry.isDirectory()?files.push(...scanCommands(fullPath,baseDir)):entry.isFile()&&entry.name.endsWith(`.${FILE_EXT}`)&&!/\.(spec|test)\.[jt]sx?$/.test(entry.name)&&files.push(fullPath)}}catch{}return files}function pathToCommandName(filePath,commandsDir){return path.relative(commandsDir,filePath).replace(RegExp(`\\.${FILE_EXT}$`),``).replace(/\//g,`:`)}async function parseCommand(rawArgs,command){let parsedOptions={},positionalArgs=[],shortOptionMap=new Map;if(command.opts&&`shape`in command.opts){let schemaShape=command.opts.shape;for(let[key,value]of Object.entries(schemaShape)){let meta=value.meta?.();meta?.short&&typeof meta.short==`string`&&shortOptionMap.set(meta.short,key)}}for(let i=0;i<rawArgs.length;i++){let arg=rawArgs[i];if(arg.startsWith(`--`)){let[key,...valueParts]=arg.slice(2).split(`=`);if(valueParts.length>0)parsedOptions[key]=valueParts.join(`=`);else{let nextArg=rawArgs[i+1];nextArg&&!nextArg.startsWith(`-`)?(parsedOptions[key]=nextArg,i++):parsedOptions[key]=!0}}else if(arg.startsWith(`-`)&&arg.length>1){let shortOptions=arg.slice(1);if(shortOptions.length>1)for(let shortOpt of shortOptions){let longOpt=shortOptionMap.get(shortOpt);longOpt&&(parsedOptions[longOpt]=!0)}else{let shortOpt=shortOptions,longOpt=shortOptionMap.get(shortOpt)||shortOpt,nextArg=rawArgs[i+1];nextArg&&!nextArg.startsWith(`-`)?(parsedOptions[longOpt]=nextArg,i++):parsedOptions[longOpt]=!0}}else positionalArgs.push(arg)}let options={};if(command.opts&&`safeParse`in command.opts){let result=command.opts.safeParse(parsedOptions);if(!result.success){for(let issue of result.error.issues){let field=issue.path.join(`.`);console.error(colors.red(`Error:\n --${field} => ${issue.message.toLowerCase()}`))}process.exit(1)}options=result.data}let args=[];if(command.args&&`safeParse`in command.args){let result=command.args.safeParse(positionalArgs);if(!result.success){for(let issue of result.error.issues){let field=issue.path.join(`.`);console.error(colors.red(`Error:\n ${field} => ${issue.message.toLowerCase()}`))}process.exit(1)}args=result.data}return{args,options}}function showHelp(container,commands){console.log(`${colors.bold(container.config.APP_NAME)} - ${container.config.APP_DESC}\n`),console.log(`Commands:`);let maxLength=0;for(let[name,cmd]of commands){let aliasesText=cmd.aliases&&cmd.aliases.length>0?` (${cmd.aliases.join(`, `)})`:``,totalLength=name.length+aliasesText.length;maxLength=Math.max(maxLength,totalLength)}maxLength+=8;for(let[name,cmd]of commands){let aliasesText=cmd.aliases&&cmd.aliases.length>0?` (${cmd.aliases.join(`, `)})`:``,aliases=cmd.aliases&&cmd.aliases.length>0?colors.dim(` (${cmd.aliases.join(`, `)})`):``,nameWithAliases=`${colors.cyan(name)}${aliases}`,plainLength=name.length+aliasesText.length,padding=` `.repeat(Math.max(0,maxLength-plainLength));console.log(` ${nameWithAliases}${padding}${cmd.description||`No description`}`)}}function showCommandHelp(commandName,command){console.log(`${command.description||`No description`}`),console.log();let usage=`Usage: ${colors.cyan(commandName)}`;if(command.args){let def=command.args._def,typeName=def?.typeName||def?.type;if(typeName===`ZodTuple`||typeName===`tuple`){let items=def.items||[];for(let item of items){let zodItem=item,name=zodItem.meta?.()?.name||zodItem._def?.description||`arg`,isOptional=zodItem.isOptional?.()??!1;usage+=isOptional?` [${name}]`:` <${name}>`}if(def.rest){let restDesc=def.rest._def?.description||`args`;usage+=` [${restDesc}...]`}}else if(typeName===`ZodArray`||typeName===`array`){let displayName=command.args.meta?.()?.name||`args`,minItems=def.minLength?.value||0;minItems===0?usage+=` [${displayName}...]`:minItems===1?usage+=` <${displayName}>`:usage+=` <${displayName}...>`}}if(command.opts&&(usage+=` [options]`),console.log(usage),console.log(),command.args){let def=command.args._def,typeName=def?.typeName||def?.type;if(typeName===`ZodTuple`||typeName===`tuple`||typeName===`ZodArray`||typeName===`array`){if(console.log(`Arguments:`),typeName===`ZodTuple`||typeName===`tuple`){let items=def.items||[];for(let i=0;i<items.length;i++){let item=items[i],meta=item.meta?.(),name=meta?.name||item._def?.description||`arg${i+1}`,description=meta?.description||item._def?.description||name,required=item.isOptional?.()??!1?``:` (required)`;console.log(` ${colors.cyan(name)}${colors.dim(required)}\t\t${description}`)}if(def.rest){let restDesc=def.rest._def?.description||`additional args`;console.log(` ${colors.cyan(`${restDesc}...`)}\t\t${restDesc}`)}}else{let meta=command.args.meta?.(),displayName=meta?.name||`args`,description=meta?.description||``,minItems=def.minLength?.value||0,argDisplay=displayName;(minItems===0||minItems>1)&&(argDisplay=`${displayName}...`);let required=minItems>0?` (required)`:``;console.log(` ${colors.cyan(argDisplay)}${colors.dim(required)}\t\t${description}`)}console.log()}}if(console.log(`Options:`),console.log(` ${colors.cyan(`-h, --help`)}\t\t\tShow help for this command`),command.opts&&`shape`in command.opts){let schemaShape=command.opts.shape;for(let[key,schema]of Object.entries(schemaShape)){let zodType=schema,meta=zodType.meta?.(),isOptional=zodType.isOptional?.()??!0,isBoolean=zodType._def?.typeName===`ZodBoolean`,short=meta?.short?`-${meta.short}, `:` `,long=`--${key}`,value=isBoolean?``:` <value>`,required=isOptional?``:` (required)`,defaultValue=zodType._def?.defaultValue,defaultStr=defaultValue===void 0?``:`(default: ${JSON.stringify(defaultValue)})`,description=meta?.description||``;console.log(` ${colors.cyan(short+long+value)}\t\t${description} ${colors.dim(required+defaultStr)}`)}}}async function loadCommands(commandsDir,commands,aliases){let commandFiles=scanCommands(commandsDir);for(let filePath of commandFiles)try{let command=(await import(filePath)).default;if(command&&typeof command==`object`&&`run`in command){let commandName=pathToCommandName(filePath,commandsDir);if(commands.set(commandName,command),command.aliases)for(let alias of command.aliases)aliases.set(alias,commandName)}}catch(error){console.warn(colors.yellow(`Warning: Could not load command from ${filePath}: ${error instanceof Error?error.message:String(error)}`))}}async function defineCli(opts){let commands=new Map,aliases=new Map;for(let[name,command]of Object.entries(BUILTIN_COMMANDS))if(commands.set(name,command),command.aliases)for(let alias of command.aliases)aliases.set(alias,name);return await loadCommands(opts.commandsDir,commands,aliases),await loadEvents({container:opts.container}),DBOS.setConfig({name:opts.container.config.APP_NAME,runAdminServer:!1,systemDatabaseUrl:opts.container.worker.dbUrl}),await loadWorkflows({container:opts.container,dbos:DBOS}),{async run(args=process.argv.slice(2)){let[cmdName,...cmdArgs]=args;if(!cmdName||cmdName===`--help`||cmdName===`-h`){showHelp(opts.container,commands);return}let resolvedName=aliases.get(cmdName)||cmdName,command=commands.get(resolvedName);if(command||(console.error(colors.red(`Unknown command: ${cmdName}\n`)),showHelp(opts.container,commands),process.exit(1)),cmdArgs.includes(`--help`)||cmdArgs.includes(`-h`)){showCommandHelp(resolvedName,command);return}try{let{args:parsedArgs,options}=await parseCommand(cmdArgs,command),ctx=defineCommandContext(parsedArgs,options,opts.container);await command.run(ctx)}catch(error){error instanceof Error&&(console.error(colors.red(`Error:`),error.message),(error.message.includes(`argument`)||error.message.includes(`Invalid arguments`))&&(console.log(),console.log(`Run '${colors.cyan(`${resolvedName} --help`)}' to see usage information.`))),process.exit(1)}},showHelp(){showHelp(opts.container,commands)},showCommandHelp(command){showCommandHelp(Array.from(commands.entries()).find(([_,cmd])=>cmd===command)?.[0]||`unknown`,command)}}}const require$1=createRequire(import.meta.url),{GlobalLogger}=require$1(join(dirname(dirname(dirname(require$1.resolve(`@dbos-inc/dbos-sdk`)))),`dist/src/telemetry/logs`));async function main(){let{defineContainer}=await import(join(process.cwd(),APPOS_DIR,`main.${FILE_EXT}`)),container=await defineContainer();GlobalLogger.prototype.info=(logEntry,metadata)=>{if(typeof logEntry==`string`){if(logEntry.startsWith(`Initializing DBOS`)||logEntry.startsWith(`System Database URL:`)||logEntry.startsWith(`Executor ID:`)||logEntry.startsWith(`Application version:`)||logEntry.startsWith(`No workflows to recover from application`)||logEntry.startsWith(`DBOS launched!`))return;container.logger.info(metadata,logEntry)}else container.logger.info(metadata,JSON.stringify(logEntry))},await(await defineCli({container,commandsDir:join(process.cwd(),APPOS_DIR,COMMANDS_DIR)})).run(process.argv.slice(2))}try{await main()}catch(err){console.error(err),process.exit(1)}export{};
|
|
338
|
+
`)};`,file=path.join(migrationFolder,`migration.sql`),snapshotFile=path.join(migrationFolder,`snapshot.json`);return await fs$1.writeFile(file,sqlContent),await fs$1.writeFile(snapshotFile,JSON.stringify(currentJson,null,2)),file});ctx.success(migrationFile),createdAny=!0}catch(error){ctx.fail(error instanceof Error?error.message:String(error))}}createdAny||ctx.info(`Nothing to generate`)}async function runMigrations(opts){let{ctx,migrationType}=opts,{container}=ctx,databaseNames=Object.keys(container.db),originalLogLevel=container.logger.level;container.logger.level=`silent`,migrationType===`schema`&&await(0,import_system_database.ensureSystemDatabase)(container.worker.dbUrl,container.logger);let hasAnyMigrations=(await Promise.all(databaseNames.map(async name=>{let{migrationsFolder}=defineMigrationOpts(name,migrationType);return exists(migrationsFolder)}))).some(Boolean);if(databaseNames.length===0||!hasAnyMigrations){ctx.info(`Nothing to migrate`),container.logger.level=originalLogLevel;return}for(let name of databaseNames){let db=container.db[name],migrationOpts=defineMigrationOpts(name,migrationType);if(await exists(migrationOpts.migrationsFolder))try{await ctx.task(name,async()=>{await migrate(db,migrationOpts)})}catch(error){ctx.fail(error instanceof Error?error.message:String(error))}}container.logger.level=originalLogLevel}async function runSeeds(opts){let{ctx}=opts,{container}=ctx,databaseNames=Object.keys(container.db);if(databaseNames.length===0){ctx.info(`Nothing to seed`);return}for(let name of databaseNames)await ensureDatabaseScaffolding(name);for(let name of databaseNames){let seedPath=path.join(process.cwd(),APPOS_DIR,DATABASES_DIR,name,`seed.ts`);try{await ctx.task(name,async()=>{let seedModule=await import(seedPath),seedFn=seedModule.default||seedModule.seed;if(typeof seedFn!=`function`)throw Error(`seed file does not export a function`);await seedFn(container)})}catch(error){ctx.fail(error instanceof Error?error.message:String(error))}}}var gen_default$1=defineCommand({description:`Generate database data migrations`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`db:data:gen`),await generateMigrations({ctx,migrationType:`data`}),await ctx.cleanup(),ctx.outro()}}),migrate_default$1=defineCommand({description:`Run pending database data migrations`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`db:data:migrate`),await runMigrations({ctx,migrationType:`data`}),await ctx.cleanup(),ctx.outro()}}),gen_default=defineCommand({description:`Generate database schema migrations from schema changes`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`db:schema:gen`),await generateMigrations({ctx,migrationType:`schema`}),await ctx.cleanup(),ctx.outro()}}),migrate_default=defineCommand({description:`Run pending database schema migrations`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`db:schema:migrate`),await runMigrations({ctx,migrationType:`schema`}),await ctx.cleanup(),ctx.outro()}}),seed_default=defineCommand({description:`Seed databases with initial data`,args:z.tuple([]),opts:z.object({}),async run(ctx){await runSeeds({ctx}),await ctx.cleanup()}}),down_default=defineCommand({description:`Teardown the infrastructure`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`down`);try{await ctx.spinner(`Stopping containers`,()=>ctx.exec(`docker`,[`compose`,`--profile=infra`,`down`,`--remove-orphans`,`--timeout=0`]))}catch(error){ctx.fail(error instanceof Error?error.message:String(error))}ctx.outro()}}),preview_default=defineCommand({description:`Preview the application`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`preview`),ctx.step(`Starting containers (Ctrl+C to stop)`);let proc=spawn(`docker`,[`compose`,`--profile=app`,`up`,`--build`],{stdio:`inherit`,cwd:process.cwd()}),isShuttingDown=!1,cleanup=async()=>{if(!isShuttingDown){isShuttingDown=!0,proc.kill(`SIGTERM`),ctx.step(`Stopping containers`);try{await ctx.exec(`docker`,[`compose`,`--profile=app`,`down`,`--remove-orphans`,`--timeout=0`])}catch{}process.exit(0)}};process.on(`SIGINT`,cleanup),process.on(`SIGTERM`,cleanup),proc.on(`close`,()=>{isShuttingDown||cleanup()}),await new Promise(()=>{})}}),version=`0.3.2-0`;const runtimeInfo$1=`(AppOS v${version} | Node ${process.version})`;var repl_default=defineCommand({description:`Start an interactive REPL with container access`,aliases:[`r`],args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`repl`);let{container}=ctx,available=[],dbNames=Object.keys(container.db);dbNames.length>0&&available.push(`container.db.{${dbNames.join(`, `)}}`);let cacheNames=Object.keys(container.cache);cacheNames.length>0&&available.push(`container.cache.{${cacheNames.join(`, `)}}`);let storageNames=Object.keys(container.storage);storageNames.length>0&&available.push(`container.storage.{${storageNames.join(`, `)}}`);let otherKeys=Object.keys(container).filter(k=>k!==`db`&&k!==`cache`&&k!==`storage`).map(k=>`container.${k}`);available.push(...otherKeys),ctx.info(`Starting REPL ${runtimeInfo$1}`),ctx.info(`Available: ${available.join(`, `)}`),ctx.line(``);let replServer=repl.start({prompt:`appos(${container.config.APP_NAME})> `,useColors:!0,useGlobal:!0,breakEvalOnSigint:!0});replServer.context.container=container,replServer.on(`exit`,async()=>{await ctx.cleanup(),ctx.info(`Bye`),process.exit(0)}),await new Promise(()=>{})}}),reset_default=defineCommand({description:`Reset the infrastructure`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`reset`);try{await ctx.spinner(`Stopping containers`,()=>ctx.exec(`docker`,[`compose`,`--profile=infra`,`down`,`--remove-orphans`,`--timeout=0`]))}catch(error){ctx.fail(error instanceof Error?error.message:String(error))}try{await ctx.spinner(`Starting containers`,()=>ctx.exec(`docker`,[`compose`,`--profile=infra`,`up`,`--wait`]))}catch(error){ctx.fail(error instanceof Error?error.message:String(error))}ctx.step(`Running schema migrations`),await runMigrations({ctx,migrationType:`schema`}),ctx.step(`Seeding databases`),await runSeeds({ctx}),await ctx.cleanup(),ctx.outro()}});function getErrorStatus(error){if(error&&typeof error==`object`&&`status`in error){let status=error.status;if(typeof status==`number`)return status}return error instanceof SyntaxError?400:500}function defineErrorHandlerMiddleware(logger){return(error,req,res,next)=>{let userAgent=req.headers[`user-agent`];if(logger.error({error:error.message,stack:error.stack,url:req.url,method:req.method,userAgent:typeof userAgent==`string`?userAgent:userAgent?.[0],ip:req.ip},`Unhandled request error`),res.headersSent)return next(error);let isDevelopment=process.env.NODE_ENV!==`production`;res.status(getErrorStatus(error)).json({error:`Internal server error`,...isDevelopment&&{message:error.message,stack:error.stack}})}}function defineHealthMiddleware(path$2=`/health`){return(req,res,next)=>{if(req.path!==path$2)return next();res.status(200).json({status:`healthy`,timestamp:new Date().toISOString(),uptime:process.uptime()})}}function defineI18nMiddleware(i18n){return(req,res,next)=>{let acceptLanguage=req.headers[`accept-language`],detectedLng=`en`;if(acceptLanguage){let parsed=acceptLanguage.split(`,`)[0].split(`;`)[0].trim();parsed&&parsed.length>0&&(detectedLng=parsed)}req.dir=i18n.dir(detectedLng),req.language=detectedLng,req.languages=i18n.languages,req.t=i18n.getFixedT(detectedLng),next()}}var instrumentation_exports={};import*as import__opentelemetry_api from"@opentelemetry/api";__reExport(instrumentation_exports,import__opentelemetry_api),register(`@opentelemetry/instrumentation/hook.mjs`,import.meta.url);const service={name:process.env.APP_NAME||`appos`,version:process.env.APP_VERSION||`development`};new NodeSDK({resource:resourceFromAttributes({[ATTR_SERVICE_NAME]:service.name,[ATTR_SERVICE_VERSION]:service.version}),spanProcessors:[new BatchSpanProcessor(new OTLPTraceExporter({url:process.env.OTEL_EXPORTER_OTLP_ENDPOINT||`http://localhost:4318/v1/traces`,headers:process.env.OTEL_EXPORTER_OTLP_HEADERS?JSON.parse(process.env.OTEL_EXPORTER_OTLP_HEADERS):void 0}))],instrumentations:[getNodeAutoInstrumentations({"@opentelemetry/instrumentation-fs":{enabled:!1},"@opentelemetry/instrumentation-dns":{enabled:!1}}),new PinoInstrumentation({logHook:(_span,record)=>{record[`service.name`]=service.name}})]}).start();const CF_COUNTRY_HEADER=`cf-ipcountry`;function extractRequestMetadata(req,config){config?.injectCountry&&!req.headers[CF_COUNTRY_HEADER]&&(req.headers[CF_COUNTRY_HEADER]=config.injectCountry);let forwardedFor=req.headers[`x-forwarded-for`],ipAddress;ipAddress=typeof forwardedFor==`string`?forwardedFor.split(`,`)[0].trim():Array.isArray(forwardedFor)&&forwardedFor.length>0?forwardedFor[0].split(`,`)[0].trim():req.socket.remoteAddress||``;let userAgent=req.headers[`user-agent`]||``,country=req.headers[CF_COUNTRY_HEADER]||``;return{ipAddress,userAgent,country}}function defineRequestLoggerMiddleware(logger,config){let tracer;try{tracer=instrumentation_exports.trace.getTracer(`request-logger`)}catch(error){logger.warn({error},`Failed to initialize OpenTelemetry tracer`),tracer=null}return(req,res,next)=>{let metadata=extractRequestMetadata(req,config);req.metadata=metadata;let startTime=Date.now(),span=null,requestId;if(tracer)try{span=tracer.startSpan(`${req.method} ${req.path}`,{kind:instrumentation_exports.SpanKind.SERVER,attributes:{"http.method":req.method,"http.url":req.url,"http.target":req.path,"http.host":(typeof req.headers.host==`string`?req.headers.host:req.headers[`:authority`])||`unknown`,"http.scheme":req.protocol,"http.user_agent":metadata.userAgent||`unknown`,"http.client_ip":metadata.ipAddress,"http.client_country":metadata.country}}),requestId=span.spanContext().traceId}catch(error){logger.warn({error},`Failed to create OpenTelemetry span`),span=null,requestId=crypto.randomUUID()}else logger.info(`OpenTelemetry tracer not available, skipping span creation`),span=null,requestId=crypto.randomUUID();let ctx=span?instrumentation_exports.trace.setSpan(instrumentation_exports.context.active(),span):instrumentation_exports.context.active(),skipLogging=(path$2=>path$2.startsWith(`/@`)||path$2.startsWith(`/node_modules/`)||path$2.startsWith(`/assets/`)||path$2.startsWith(`/public/`)||path$2.match(/\.(js|css|ico|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|map)$/)||path$2.includes(`/locales/`)||path$2.endsWith(`.ts`)||path$2.endsWith(`.tsx`)||path$2.endsWith(`.json`))(req.path);res.setHeader(`X-Request-Id`,requestId),req.requestId=requestId,res.on(`finish`,()=>{let duration=Date.now()-startTime;if(span)try{span.setAttributes({"http.status_code":res.statusCode,"http.response_content_length":res.get(`content-length`)||0}),res.statusCode>=400?span.setStatus({code:instrumentation_exports.SpanStatusCode.ERROR,message:`HTTP ${res.statusCode}`}):span.setStatus({code:instrumentation_exports.SpanStatusCode.OK}),span.end()}catch(error){logger.warn({error},`Failed to update OpenTelemetry span`)}skipLogging||instrumentation_exports.context.with(ctx,()=>{logger.info({requestId,method:req.method,url:req.path,status:res.statusCode,duration:`${duration}ms`},`Request completed`)})}),res.on(`close`,()=>{if(!(res.writableEnded||res.finished)){let duration=Date.now()-startTime;if(span)try{span.setStatus({code:instrumentation_exports.SpanStatusCode.ERROR,message:`Request closed unexpectedly`}),span.end()}catch(error){logger.warn({error},`Failed to update OpenTelemetry span`)}skipLogging||instrumentation_exports.context.with(ctx,()=>{logger.warn({requestId,method:req.method,url:req.path,duration:`${duration}ms`},`Request closed unexpectedly`)})}});let originalNext=next;next=error=>{if(error){let duration=Date.now()-startTime;if(span)try{span.recordException(error),span.setStatus({code:instrumentation_exports.SpanStatusCode.ERROR,message:error instanceof Error?error.message:String(error)}),span.end()}catch(spanError){logger.warn({error:spanError},`Failed to record exception in span`)}skipLogging||instrumentation_exports.context.with(ctx,()=>{logger.error({requestId,method:req.method,url:req.path,status:res.statusCode||500,duration:`${duration}ms`,error:error instanceof Error?error.message:String(error)},`Request failed`)})}originalNext(error)},instrumentation_exports.context.with(ctx,()=>next())}}function defineShutdownMiddleware(logger,isShuttingDown){return(req,res,next)=>{if(isShuttingDown()){logger.info({url:req.url,method:req.method},`Rejecting request during shutdown`),res.status(503).set(`Connection`,`close`).json({error:`Server is shutting down`});return}next()}}function defineTimeoutMiddleware(logger,timeoutMs=3e4){return(req,res,next)=>{let timeoutFired=!1,timeoutId=setTimeout(()=>{timeoutFired=!0,logger.warn({url:req.url,method:req.method,timeout:timeoutMs},`Request timeout`),res.headersSent||res.status(408).json({error:`Request timeout`}),res.removeListener(`finish`,clearTimeoutHandler),res.removeListener(`close`,clearTimeoutHandler)},timeoutMs),clearTimeoutHandler=()=>{timeoutFired||clearTimeout(timeoutId)};res.on(`finish`,clearTimeoutHandler),res.on(`close`,clearTimeoutHandler),next()}}function getMiddlewareName(filename){return camelCase(basename(filename,`.ts`).replace(/^\d+_/,``))}async function loadMiddleware(middlewareDir,container,app){try{await fs$1.access(middlewareDir)}catch{return}let files=[];for await(let file of glob(`${middlewareDir}/**/*.${FILE_EXT}`))!file.endsWith(`.test.ts`)&&!file.endsWith(`.spec.ts`)&&!file.endsWith(`.test.js`)&&!file.endsWith(`.spec.js`)&&files.push(file);files.sort((a,b)=>basename(a).localeCompare(basename(b)));for(let file of files)try{let middleware=(await import(file)).default;if(middleware&&typeof middleware.handler==`function`){let name=middleware.name??getMiddlewareName(file);container.logger.debug({name,file},`Loading user middleware`),app.use(middleware.handler(container))}else container.logger.warn({file},`Middleware file missing default export with handler function`)}catch(err){throw container.logger.error({file,error:err instanceof Error?err.message:err},`Failed to load middleware`),err}}const outputDir=resolve(PUBLIC_DIR,`openapi`);function extractVersion(path$2){return path$2.match(/^\/(v\d+)/)?.[1]}function convertToOpenAPIPath(reactRouterPath){return reactRouterPath.replace(/:(\w+)/g,`{$1}`)}function buildPathObject(path$2,spec){let openApiPath=convertToOpenAPIPath(path$2),methodConfig={summary:spec.summary,description:spec.description};(spec.request?.params||spec.request?.query||spec.request?.headers)&&(methodConfig.requestParams={},spec.request.params&&(methodConfig.requestParams.path=spec.request.params),spec.request.query&&(methodConfig.requestParams.query=spec.request.query),spec.request.headers&&(methodConfig.requestParams.header=spec.request.headers)),spec.request?.body&&(methodConfig.requestBody={content:{"application/json":{schema:spec.request.body}}}),methodConfig.responses={};for(let[statusCode,response]of Object.entries(spec.responses))methodConfig.responses[statusCode]={description:response.description,content:{"application/json":{schema:response.content[`application/json`].schema}}};return{path:openApiPath,config:methodConfig}}function generateOpenAPIDocument(routes,version$1,config){let paths={};for(let route of routes){if(route.version!==version$1)continue;let apiPath=route.path.replace(`/${version$1}`,``)||`/`;for(let spec of route.openAPISpec){let{path:openApiPath,config:methodConfig}=buildPathObject(apiPath,spec);paths[openApiPath]||(paths[openApiPath]={}),paths[openApiPath][spec.method]=methodConfig}}return createDocument({openapi:`3.1.0`,info:config?.info||{title:`API ${version$1.toUpperCase()}`,version:version$1.replace(`v`,``),description:`OpenAPI specification for ${version$1} API`},servers:config?.servers||[{url:`http://localhost:8000/${version$1}`,description:process.env.NODE_ENV===`production`?`Production server`:`Development server`}],paths})}async function scanAPIRoutes(container){let fullPath=resolve(APPOS_DIR,ROUTES_DIR);try{await access(fullPath)}catch(error){return container.logger.error({error},`OpenAPI routes directory not found`),[]}let routeManifest=await remixRoutesOptionAdapter(defineRoutes=>flatRoutes(ROUTES_DIR,defineRoutes,{appDir:APPOS_DIR,ignoredRouteFiles:[`**/.*`,`**/*.{spec,test}.{ts,tsx}`,`**/*-????????.{js,ts}`]})),routes=[];for(let[routeId,route]of Object.entries(routeManifest)){let absolutePath=resolve(APPOS_DIR,route.file).replace(/.ts$/,process.env.NODE_ENV===`production`?`.js`:`.ts`);try{let exported=(await import(absolutePath)).default;if(!exported||isEmpty(exported)||/\/openapi.(j|t)s$/.test(absolutePath))continue;if(!exported?.handlers&&!exported?.openAPISpec){container.logger.warn(`Missing default export with 'defineOpenAPI' for '${APPOS_DIR}/${route.file}'`);continue}let routePath=`/${route.path}`;routes.push({path:routePath,filePath:absolutePath,handlers:exported.handlers,openAPISpec:exported.openAPISpec,version:extractVersion(routePath)})}catch(error){container.logger.error(error,`Error loading route ${route.file}:`)}}return routes}function registerRoutes(app,routes){for(let route of routes)for(let[method,handler]of Object.entries(route.handlers))app[method.toLowerCase()](route.path,handler)}async function loadVersionConfig(container,version$1){let configPath=resolve(APPOS_DIR,ROUTES_DIR,`${version$1}+/openapi.${FILE_EXT}`);try{let configFn=(await import(configPath)).default;return configFn?configFn(container):void 0}catch{return}}async function writeOpenAPISpecs(container,routes){let versions=[...new Set(routes.map(r=>r.version).filter(Boolean))];versions.length>0&&await mkdir(outputDir,{recursive:!0});for(let version$1 of versions){let doc=generateOpenAPIDocument(routes,version$1,await loadVersionConfig(container,version$1));await writeFile(join(outputDir,`${version$1}.json`),JSON.stringify(doc,null,2),`utf-8`)}}async function loadAndRegisterAPIRoutes(app){let routes=await scanAPIRoutes(app.locals.container);registerRoutes(app,routes),process.env.NODE_ENV!==`production`&&await writeOpenAPISpecs(app.locals.container,routes)}async function defineServer({container}){let isShuttingDown=!1,{host=`0.0.0.0`,port,timeout=3e4,bodyLimit=`1mb`,healthPath=`/health`,cors:corsConfig,helmet:helmetConfig,rateLimit:rateLimitConfigs,redisUrl}=container.server,app=express();app.locals.container=container;let redisClient=null;if(redisUrl&&rateLimitConfigs)try{redisClient=createClient({url:redisUrl}),redisClient.on(`error`,err=>{container.logger.error({error:err.message},`Redis client error`)}),await redisClient.connect(),container.logger.info(`Connected to Redis for rate limiting`)}catch(err){container.logger.error({error:err instanceof Error?err.message:err},`Failed to connect to Redis, falling back to in-memory rate limiting`),redisClient=null}if(app.use(defineShutdownMiddleware(container.logger,()=>isShuttingDown),defineTimeoutMiddleware(container.logger,timeout),defineRequestLoggerMiddleware(container.logger),defineHealthMiddleware(healthPath)),helmetConfig!==void 0&&app.use(helmet(helmetConfig)),corsConfig!==void 0&&app.use(cors(corsConfig)),rateLimitConfigs)for(let[index,config]of rateLimitConfigs.entries()){let limiter=rateLimit({windowMs:config.windowMs??60*1e3,limit:config.limit??100,standardHeaders:config.standardHeaders??`draft-8`,legacyHeaders:config.legacyHeaders??!1,skip:config.skip,keyGenerator:config.keyGenerator,handler:config.handler,message:config.message,statusCode:config.statusCode,requestPropertyName:config.requestPropertyName,skipFailedRequests:config.skipFailedRequests,skipSuccessfulRequests:config.skipSuccessfulRequests,requestWasSuccessful:config.requestWasSuccessful,validate:config.validate,store:redisClient?new RedisStore({sendCommand:(...args)=>redisClient.sendCommand(args),prefix:`rl:${index}:`}):config.store});app.use(limiter)}if(app.use(defineI18nMiddleware(container.i18n),express.json({limit:bodyLimit}),express.urlencoded({extended:!0,limit:bodyLimit})),await loadMiddleware(join(process.cwd(),`api`,`middleware`),container,app),app.get(`/`,(_req,res)=>{res.json({message:`${container.config.APP_NAME} server is running.`})}).all(`${container.auth.options.basePath}/*`,toNodeHandler(container.auth)),await loadAndRegisterAPIRoutes(app),app.use(express.static(resolve(PUBLIC_DIR))),process.env.NODE_ENV!==`production`){let{defineYouchErrorHandler}=await import(`./youch-handler-BadUgHb0.mjs`);app.use(defineYouchErrorHandler(container.logger))}else app.use(defineErrorHandlerMiddleware(container.logger));return{app,host,port,async start(){await new Promise((resolve$1,reject)=>{try{app.listen(port,host,()=>{resolve$1()})}catch(err){reject(err)}})},async close(){if(isShuttingDown=!0,app.uwsApp.close(),redisClient)try{await redisClient.destroy()}catch(err){container.logger.warn({error:err instanceof Error?err.message:err},`Error disconnecting from Redis`)}}}}function defineWorker(){return{async start(){await DBOS.launch()},async close(){await DBOS.shutdown()}}}const runtimeInfo=`(AppOS v${version} | Node ${process.version})`;var start_default=defineCommand({description:`Start the HTTP server and/or DBOS workflow worker`,aliases:[`s`],args:z.tuple([]),opts:z.object({service:z.enum([`server`,`worker`]).optional().meta({short:`s`,description:`Start only a specific service (default: both)`})}),async run(ctx){let{container,opts}=ctx,{logger}=container,startServer=!opts.service||opts.service===`server`,startWorker=!opts.service||opts.service===`worker`,server=null,worker=null,isShuttingDown=!1,shutdown=async signal=>{if(!isShuttingDown){isShuttingDown=!0,logger.info(`Shutdown initiated (${signal})`);try{server&&(await server.close(),logger.info(`HTTP server stopped`)),worker&&(await worker.close(),logger.info(`DBOS worker stopped`)),await ctx.cleanup()}catch(error){logger.error({error},`Shutdown error`)}process.exit(0)}};process.on(`SIGINT`,()=>shutdown(`SIGINT`)),process.on(`SIGTERM`,()=>shutdown(`SIGTERM`)),startWorker&&(worker=defineWorker(),await worker.start(),logger.info(`DBOS worker ready ${runtimeInfo}`)),startServer&&(server=await defineServer({container}),await server.start(),logger.info(`HTTP server ready at http://${server.host}:${server.port} ${runtimeInfo}`))}}),up_default=defineCommand({description:`Setup the infrastructure`,args:z.tuple([]),opts:z.object({}),async run(ctx){ctx.intro(`up`);try{await ctx.spinner(`Starting containers`,()=>ctx.exec(`docker`,[`compose`,`--profile=infra`,`up`,`--wait`]))}catch(error){ctx.fail(error instanceof Error?error.message:String(error))}ctx.step(`Running schema migrations`),await runMigrations({ctx,migrationType:`schema`}),ctx.step(`Seeding databases`),await runSeeds({ctx}),await ctx.cleanup(),ctx.outro()}});const BUILTIN_COMMANDS={build:build_default,"db:data:gen":gen_default$1,"db:data:migrate":migrate_default$1,"db:schema:gen":gen_default,"db:schema:migrate":migrate_default,"db:seed":seed_default,down:down_default,preview:preview_default,repl:repl_default,reset:reset_default,start:start_default,up:up_default};function scanCommands(dir,baseDir=dir){let files=[];try{let entries=fs.readdirSync(dir,{withFileTypes:!0});for(let entry of entries){let fullPath=path.join(dir,entry.name);entry.isDirectory()?files.push(...scanCommands(fullPath,baseDir)):entry.isFile()&&entry.name.endsWith(`.${FILE_EXT}`)&&!/\.(spec|test)\.[jt]sx?$/.test(entry.name)&&files.push(fullPath)}}catch{}return files}function pathToCommandName(filePath,commandsDir){return path.relative(commandsDir,filePath).replace(RegExp(`\\.${FILE_EXT}$`),``).replace(/\//g,`:`)}async function parseCommand(rawArgs,command){let parsedOptions={},positionalArgs=[],shortOptionMap=new Map;if(command.opts&&`shape`in command.opts){let schemaShape=command.opts.shape;for(let[key,value]of Object.entries(schemaShape)){let meta=value.meta?.();meta?.short&&typeof meta.short==`string`&&shortOptionMap.set(meta.short,key)}}for(let i=0;i<rawArgs.length;i++){let arg=rawArgs[i];if(arg.startsWith(`--`)){let[key,...valueParts]=arg.slice(2).split(`=`);if(valueParts.length>0)parsedOptions[key]=valueParts.join(`=`);else{let nextArg=rawArgs[i+1];nextArg&&!nextArg.startsWith(`-`)?(parsedOptions[key]=nextArg,i++):parsedOptions[key]=!0}}else if(arg.startsWith(`-`)&&arg.length>1){let shortOptions=arg.slice(1);if(shortOptions.length>1)for(let shortOpt of shortOptions){let longOpt=shortOptionMap.get(shortOpt);longOpt&&(parsedOptions[longOpt]=!0)}else{let shortOpt=shortOptions,longOpt=shortOptionMap.get(shortOpt)||shortOpt,nextArg=rawArgs[i+1];nextArg&&!nextArg.startsWith(`-`)?(parsedOptions[longOpt]=nextArg,i++):parsedOptions[longOpt]=!0}}else positionalArgs.push(arg)}let options={};if(command.opts&&`safeParse`in command.opts){let result=command.opts.safeParse(parsedOptions);if(!result.success){for(let issue of result.error.issues){let field=issue.path.join(`.`);console.error(colors.red(`Error:\n --${field} => ${issue.message.toLowerCase()}`))}process.exit(1)}options=result.data}let args=[];if(command.args&&`safeParse`in command.args){let result=command.args.safeParse(positionalArgs);if(!result.success){for(let issue of result.error.issues){let field=issue.path.join(`.`);console.error(colors.red(`Error:\n ${field} => ${issue.message.toLowerCase()}`))}process.exit(1)}args=result.data}return{args,options}}function showHelp(container,commands){console.log(`${colors.bold(container.config.APP_NAME)} - ${container.config.APP_DESC}\n`),console.log(`Commands:`);let maxLength=0;for(let[name,cmd]of commands){let aliasesText=cmd.aliases&&cmd.aliases.length>0?` (${cmd.aliases.join(`, `)})`:``,totalLength=name.length+aliasesText.length;maxLength=Math.max(maxLength,totalLength)}maxLength+=8;for(let[name,cmd]of commands){let aliasesText=cmd.aliases&&cmd.aliases.length>0?` (${cmd.aliases.join(`, `)})`:``,aliases=cmd.aliases&&cmd.aliases.length>0?colors.dim(` (${cmd.aliases.join(`, `)})`):``,nameWithAliases=`${colors.cyan(name)}${aliases}`,plainLength=name.length+aliasesText.length,padding=` `.repeat(Math.max(0,maxLength-plainLength));console.log(` ${nameWithAliases}${padding}${cmd.description||`No description`}`)}}function showCommandHelp(commandName,command){console.log(`${command.description||`No description`}`),console.log();let usage=`Usage: ${colors.cyan(commandName)}`;if(command.args){let def=command.args._def,typeName=def?.typeName||def?.type;if(typeName===`ZodTuple`||typeName===`tuple`){let items=def.items||[];for(let item of items){let zodItem=item,name=zodItem.meta?.()?.name||zodItem._def?.description||`arg`,isOptional=zodItem.isOptional?.()??!1;usage+=isOptional?` [${name}]`:` <${name}>`}if(def.rest){let restDesc=def.rest._def?.description||`args`;usage+=` [${restDesc}...]`}}else if(typeName===`ZodArray`||typeName===`array`){let displayName=command.args.meta?.()?.name||`args`,minItems=def.minLength?.value||0;minItems===0?usage+=` [${displayName}...]`:minItems===1?usage+=` <${displayName}>`:usage+=` <${displayName}...>`}}if(command.opts&&(usage+=` [options]`),console.log(usage),console.log(),command.args){let def=command.args._def,typeName=def?.typeName||def?.type;if(typeName===`ZodTuple`||typeName===`tuple`||typeName===`ZodArray`||typeName===`array`){if(console.log(`Arguments:`),typeName===`ZodTuple`||typeName===`tuple`){let items=def.items||[];for(let i=0;i<items.length;i++){let item=items[i],meta=item.meta?.(),name=meta?.name||item._def?.description||`arg${i+1}`,description=meta?.description||item._def?.description||name,required=item.isOptional?.()??!1?``:` (required)`;console.log(` ${colors.cyan(name)}${colors.dim(required)}\t\t${description}`)}if(def.rest){let restDesc=def.rest._def?.description||`additional args`;console.log(` ${colors.cyan(`${restDesc}...`)}\t\t${restDesc}`)}}else{let meta=command.args.meta?.(),displayName=meta?.name||`args`,description=meta?.description||``,minItems=def.minLength?.value||0,argDisplay=displayName;(minItems===0||minItems>1)&&(argDisplay=`${displayName}...`);let required=minItems>0?` (required)`:``;console.log(` ${colors.cyan(argDisplay)}${colors.dim(required)}\t\t${description}`)}console.log()}}if(console.log(`Options:`),console.log(` ${colors.cyan(`-h, --help`)}\t\t\tShow help for this command`),command.opts&&`shape`in command.opts){let schemaShape=command.opts.shape;for(let[key,schema]of Object.entries(schemaShape)){let zodType=schema,meta=zodType.meta?.(),isOptional=zodType.isOptional?.()??!0,isBoolean=zodType._def?.typeName===`ZodBoolean`,short=meta?.short?`-${meta.short}, `:` `,long=`--${key}`,value=isBoolean?``:` <value>`,required=isOptional?``:` (required)`,defaultValue=zodType._def?.defaultValue,defaultStr=defaultValue===void 0?``:`(default: ${JSON.stringify(defaultValue)})`,description=meta?.description||``;console.log(` ${colors.cyan(short+long+value)}\t\t${description} ${colors.dim(required+defaultStr)}`)}}}async function loadCommands(commandsDir,commands,aliases){let commandFiles=scanCommands(commandsDir);for(let filePath of commandFiles)try{let command=(await import(filePath)).default;if(command&&typeof command==`object`&&`run`in command){let commandName=pathToCommandName(filePath,commandsDir);if(commands.set(commandName,command),command.aliases)for(let alias of command.aliases)aliases.set(alias,commandName)}}catch(error){console.warn(colors.yellow(`Warning: Could not load command from ${filePath}: ${error instanceof Error?error.message:String(error)}`))}}async function defineCli(opts){let commands=new Map,aliases=new Map;for(let[name,command]of Object.entries(BUILTIN_COMMANDS))if(commands.set(name,command),command.aliases)for(let alias of command.aliases)aliases.set(alias,name);return await loadCommands(opts.commandsDir,commands,aliases),await loadEvents({container:opts.container}),DBOS.setConfig({name:opts.container.config.APP_NAME,runAdminServer:!1,systemDatabaseUrl:opts.container.worker.dbUrl}),await loadWorkflows({container:opts.container,dbos:DBOS}),{async run(args=process.argv.slice(2)){let[cmdName,...cmdArgs]=args;if(!cmdName||cmdName===`--help`||cmdName===`-h`){showHelp(opts.container,commands);return}let resolvedName=aliases.get(cmdName)||cmdName,command=commands.get(resolvedName);if(command||(console.error(colors.red(`Unknown command: ${cmdName}\n`)),showHelp(opts.container,commands),process.exit(1)),cmdArgs.includes(`--help`)||cmdArgs.includes(`-h`)){showCommandHelp(resolvedName,command);return}try{let{args:parsedArgs,options}=await parseCommand(cmdArgs,command),ctx=defineCommandContext(parsedArgs,options,opts.container);await command.run(ctx)}catch(error){error instanceof Error&&(console.error(colors.red(`Error:`),error.message),(error.message.includes(`argument`)||error.message.includes(`Invalid arguments`))&&(console.log(),console.log(`Run '${colors.cyan(`${resolvedName} --help`)}' to see usage information.`))),process.exit(1)}},showHelp(){showHelp(opts.container,commands)},showCommandHelp(command){showCommandHelp(Array.from(commands.entries()).find(([_,cmd])=>cmd===command)?.[0]||`unknown`,command)}}}const require$1=createRequire(import.meta.url),{GlobalLogger}=require$1(join(dirname(dirname(dirname(require$1.resolve(`@dbos-inc/dbos-sdk`)))),`dist/src/telemetry/logs`));async function main(){let{defineContainer}=await import(join(process.cwd(),APPOS_DIR,`main.${FILE_EXT}`)),container=await defineContainer();GlobalLogger.prototype.info=(logEntry,metadata)=>{if(typeof logEntry==`string`){if(logEntry.startsWith(`Initializing DBOS`)||logEntry.startsWith(`System Database URL:`)||logEntry.startsWith(`Executor ID:`)||logEntry.startsWith(`Application version:`)||logEntry.startsWith(`No workflows to recover from application`)||logEntry.startsWith(`DBOS launched!`))return;container.logger.info(metadata,logEntry)}else container.logger.info(metadata,JSON.stringify(logEntry))},await(await defineCli({container,commandsDir:join(process.cwd(),APPOS_DIR,COMMANDS_DIR)})).run(process.argv.slice(2))}try{await main()}catch(err){console.error(err),process.exit(1)}export{};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineWorkflow as e}from"
|
|
1
|
+
import{defineWorkflow as e}from"../workflow.mjs";import t from"zod";import{join as n}from"node:path";import{ALL_FORMATS as r,BlobSource as i,Input as a}from"mediabunny";const o=e({input:t.object({blobId:t.string()}),async run({container:e,input:{blobId:t},step:o}){let s=await o(`fetch-blob`,async()=>e.storage.primary.getBlob(t));if(!s)throw Error(`Blob ${t} not found`);let c=await o(`download-blob`,async()=>e.storage.primary.downloadBlob(t));if(!c)throw Error(`Failed to download blob ${t}`);let l={};return s.contentType?.startsWith(`image/`)?l=await o(`extract-image-metadata`,async()=>{let e=(await import(`sharp`)).default,t=await e(c).metadata();return{width:t.width,height:t.height,format:t.format,hasAlpha:t.hasAlpha,space:t.space}}):s.contentType?.startsWith(`video/`)||s.contentType?.startsWith(`audio/`)?l=await o(`extract-media-metadata`,async()=>{let e=new Uint8Array(c),t=new a({source:new i(new Blob([e],{type:s.contentType||`video/mp4`})),formats:r}),n=await t.computeDuration(),o=await t.getMetadataTags(),l={},u={},d=!1,f=!1;try{let e=await t.getPrimaryVideoTrack();if(e){d=!0;let t=e.displayWidth&&e.displayHeight?e.displayWidth/e.displayHeight:null;l={width:e.displayWidth,height:e.displayHeight,rotation:e.rotation,angle:e.rotation,displayAspectRatio:t}}}catch{}try{let e=await t.getPrimaryAudioTrack();e&&(f=!0,u={sampleRate:e.sampleRate,channels:e.numberOfChannels})}catch{}return{duration:n,video:d,audio:f,...l,...u,tags:o}}):s.contentType===`application/pdf`&&(l=await o(`extract-pdf-metadata`,async()=>{try{let e=await import(`pdfjs-dist/legacy/build/pdf.mjs`),t=`${n(process.cwd(),`node_modules/pdfjs-dist/standard_fonts`)}/`,r=await e.getDocument({data:new Uint8Array(c),standardFontDataUrl:t}).promise,i=await r.getMetadata(),a=(await r.getPage(1)).getViewport({scale:1}),o=i.info;return{pageCount:r.numPages,width:a.width,height:a.height,title:o?.Title||null,author:o?.Author||null,subject:o?.Subject||null,keywords:o?.Keywords||null,creator:o?.Creator||null,producer:o?.Producer||null,creationDate:o?.CreationDate||null,modificationDate:o?.ModDate||null,pdfVersion:o?.PDFFormatVersion||null}}catch(n){return e.logger.error({error:n,errorMessage:n instanceof Error?n.message:String(n),errorStack:n instanceof Error?n.stack:void 0,errorCode:n?.code,blobId:t},`Failed to extract PDF metadata`),{error:`Failed to extract PDF metadata`,errorMessage:n instanceof Error?n.message:String(n)}}})),await o(`save-metadata`,async()=>{await e.storage.primary.updateBlobMetadata(t,{...l,analyzed:!0})}),e.logger.info({blobId:t,metadata:l},`Metadata extracted`),{...l,analyzed:!0}}});export{o as extractBlobMetadata};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineWorkflow as e}from"
|
|
1
|
+
import{defineWorkflow as e}from"../workflow.mjs";import{z as t}from"zod";const n=t.object({width:t.number().optional(),height:t.number().optional(),fit:t.enum([`cover`,`contain`,`fill`,`inside`,`outside`]).optional(),position:t.enum([`top`,`right top`,`right`,`right bottom`,`bottom`,`left bottom`,`left`,`left top`,`centre`]).optional(),kernel:t.enum([`nearest`,`linear`,`cubic`,`mitchell`,`lanczos2`,`lanczos3`]).optional()}),r=t.object({resize:n.optional(),rotate:t.number().optional(),flip:t.boolean().optional(),flop:t.boolean().optional(),sharpen:t.boolean().optional(),blur:t.number().optional(),grayscale:t.boolean().optional(),format:t.enum([`jpeg`,`png`,`webp`,`avif`,`gif`]).optional(),quality:t.number().min(1).max(100).optional(),preview:t.literal(!0).optional()}),i=e({input:t.object({blobId:t.string(),transformations:r}),async run({container:e,input:{blobId:t,transformations:n},step:r}){if(!await r(`fetch-blob`,async()=>e.storage.primary.getBlob(t)))throw Error(`Blob ${t} not found`);let i=await r(`download-blob`,async()=>e.storage.primary.downloadBlob(t));if(!i)throw Error(`Failed to download blob ${t}`);let a=await r(`apply-transformations`,async()=>{let e=(await import(`sharp`)).default,t=e(i);return n.resize&&(t=t.resize({width:n.resize.width,height:n.resize.height,fit:n.resize.fit,position:n.resize.position,kernel:n.resize.kernel})),n.rotate!==void 0&&(t=t.rotate(n.rotate)),n.flip&&(t=t.flip()),n.flop&&(t=t.flop()),n.sharpen&&(t=t.sharpen()),n.blur!==void 0&&(t=t.blur(n.blur)),n.grayscale&&(t=t.grayscale()),n.format&&(t=t.toFormat(n.format,{quality:n.quality})),t.toBuffer()}),o=await r(`store-variant`,async()=>e.storage.primary.createVariant(t,n,a));return e.logger.info({blobId:t,variantId:o.id},`Image variant generated`),o}});export{i as generateImageVariant};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineWorkflow as e}from"
|
|
1
|
+
import{defineWorkflow as e}from"../workflow.mjs";import t from"zod";import{join as n}from"node:path";import{spawn as r}from"node:child_process";const i=e({input:t.object({blobId:t.string(),timeInSeconds:t.number().optional()}),async run({container:e,input:{blobId:t,timeInSeconds:i=1},step:a}){let o=await a(`fetch-blob`,async()=>e.storage.primary.getBlob(t));if(!o)throw Error(`Blob ${t} not found`);let s=await a(`download-blob`,async()=>e.storage.primary.downloadBlob(t));if(!s)throw Error(`Failed to download blob ${t}`);let c=null;if(o.contentType?.startsWith(`video/`))c=await a(`generate-video-preview`,async()=>new Promise((n,a)=>{try{let o=r(`ffmpeg`,[`-i`,`pipe:0`,`-ss`,i.toString(),`-frames:v`,`1`,`-f`,`image2pipe`,`-c:v`,`png`,`pipe:1`]),c=[],l=[];o.stdout.on(`data`,e=>{c.push(e)}),o.stderr.on(`data`,e=>{l.push(e)}),o.on(`close`,async r=>{if(r===0)try{let e=Buffer.concat(c),t=(await import(`sharp`)).default;n(await t(e).jpeg({quality:80}).toBuffer())}catch(n){e.logger.error({error:n,blobId:t},`Failed to convert video frame to JPEG`),a(n)}else{let n=Buffer.concat(l).toString(),i=Error(`FFmpeg exited with code ${r}: ${n}`);e.logger.error({error:i,blobId:t,code:r,stderr:n},`Failed to generate video preview`),a(i)}}),o.on(`error`,n=>{e.logger.error({error:n,blobId:t},`Failed to spawn FFmpeg process`),a(n)}),o.stdin.on(`error`,n=>{n.code!==`EPIPE`&&e.logger.error({error:n,blobId:t},`Failed to write to FFmpeg stdin`)}),o.stdin.write(s),o.stdin.end()}catch(n){e.logger.error({error:n,blobId:t},`Failed to generate video preview`),a(n)}}));else if(o.contentType===`application/pdf`)c=await a(`generate-pdf-preview`,async()=>{try{let e=await import(`pdfjs-dist/legacy/build/pdf.mjs`),{createCanvas:t}=await import(`canvas`),r=(await import(`sharp`)).default,i=`${n(process.cwd(),`node_modules/pdfjs-dist/standard_fonts`)}/`,a=await(await e.getDocument({data:new Uint8Array(s),standardFontDataUrl:i}).promise).getPage(1),o=a.getViewport({scale:2}),c=t(o.width,o.height),l=c.getContext(`2d`);return await a.render({canvasContext:l,viewport:o,canvas:c}).promise,await r(c.toBuffer(`image/png`)).resize(800,800,{fit:`inside`,withoutEnlargement:!0}).jpeg({quality:85}).toBuffer()}catch(n){throw e.logger.error({error:n,errorMessage:n instanceof Error?n.message:String(n),errorStack:n instanceof Error?n.stack:void 0,errorCode:n?.code,blobId:t},`Failed to generate PDF preview`),n}});else if(o.contentType?.startsWith(`image/`))c=await a(`generate-image-preview`,async()=>{let e=(await import(`sharp`)).default;return await e(s).resize(800,800,{fit:`inside`,withoutEnlargement:!0}).jpeg({quality:85}).toBuffer()});else throw Error(`Preview generation not supported for content type: ${o.contentType}`);let l=await a(`store-preview`,async()=>await e.storage.primary.createVariant(t,{preview:!0},c));return e.logger.info({blobId:t,previewId:l.id,contentType:o.contentType},`Preview generated`),l}});export{i as generatePreview};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineWorkflow as e}from"
|
|
1
|
+
import{defineWorkflow as e}from"../workflow.mjs";import t from"zod";const n=e({input:t.object({attachmentIds:t.array(t.string()).min(1)}),async run({container:e,input:{attachmentIds:t},step:n}){let r=await n(`fetch-attachments`,async()=>(await e.storage.primary.getAttachmentsByIds(t)).filter(e=>e.blob!==null).map(e=>({attachmentId:e.id,blobId:e.blob.id})));return await n(`delete-attachments`,async()=>{for(let{attachmentId:t}of r)await e.storage.primary.deleteAttachment(t)}),await n(`delete-blobs`,async()=>{for(let{blobId:t}of r)await e.storage.primary.deleteBlob(t)}),e.logger.info({attachmentIds:t,blobCount:r.length},`Attachments and blobs purged`),{purgedCount:r.length}}});export{n as purgeAttachment};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineAuthSchema as e}from"
|
|
1
|
+
import{defineAuthSchema as e}from"../auth-schema.mjs";import{defineScheduledWorkflow as t}from"../workflow.mjs";import{lt as n}from"drizzle-orm";const r=e();function i(e=`0 0 * * *`){return t({crontab:e,async run({container:e,step:t,scheduledTime:i}){let a=e.auth.auditLog?.retentionDays??90,o=new Date(i);o.setDate(o.getDate()-a);let s=o.toISOString(),c=await t(`delete-old-logs`,async()=>{let{auditLogs:t}=r.tables;return(await e.db.primary.delete(t).where(n(t.createdAt,s)).returning({id:t.id})).length});e.logger.info({deletedCount:c,retentionDays:a,cutoffDate:s},`Audit log purge completed`)}})}export{i as definePurgeAuditLogs};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineScheduledWorkflow as e}from"
|
|
1
|
+
import{defineScheduledWorkflow as e}from"../workflow.mjs";function t(t=`0 0 * * *`){return e({crontab:t,async run({container:e,step:t}){let n=new Date(Date.now()-2880*60*1e3).toISOString(),r=await t(`fetch-unattached-blobs`,async()=>e.storage.primary.getUnattachedBlobs({olderThan:n})),i=await t(`fetch-pending-blobs`,async()=>e.storage.primary.getPendingBlobs(n)),a=[...r,...i],o=0;for(let n of a)await t(`delete-blob`,async()=>{await e.storage.primary.deleteBlob(n.id),o++});e.logger.info({purgedCount:o,unattachedCount:r.length,pendingCount:i.length},`Orphaned blobs purged`)}})}export{t as definePurgeUnattachedBlobs};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineAuthSchema as e}from"
|
|
1
|
+
import{defineAuthSchema as e}from"../auth-schema.mjs";import{dbChangesEvent as t}from"../event.mjs";import{defineWorkflow as n}from"../workflow.mjs";import{z as r}from"zod";const i=e(),a=r.object({changes:r.array(r.object({_table:r.string(),old:r.record(r.string(),r.unknown()).nullable(),new:r.record(r.string(),r.unknown()).nullable()})),dbName:r.string(),organizationId:r.string().nullable(),requestId:r.string(),sessionId:r.string().nullable(),userId:r.string().nullable()});function o(e){return e.old===null?`INSERT`:e.new===null?`DELETE`:`UPDATE`}const s=n({input:a,async run({container:e,step:n,input:r}){let{dbName:a,changes:s,organizationId:c,userId:l,sessionId:u,requestId:d}=r;if(s.length===0)return{processed:0,audited:0,published:0};let f=new Date().toISOString(),p=0,m=0;for(let r of s){let s=r._table,h=o(r),g=`${a}.${s}`;e.auth.shouldAudit(g)&&(await n(`audit:${g}`,async()=>{await e.db.primary.insert(i.tables.auditLogs).values({tableName:g,action:h,oldData:r.old,newData:r.new,organizationId:c,userId:l,sessionId:u,requestId:d,createdAt:f})}),p++),await n(`event:${g}`,async()=>{await t.emit({action:h,oldData:r.old,newData:r.new,organizationId:c,tableName:g,timestamp:f,userId:l})}),m++}return{processed:s.length,audited:p,published:m}}});export{s as trackDbChanges};
|