diff --git a/functions/example/handler.ts b/functions/example/handler.ts index dc83dc2d..fd9c5550 100644 --- a/functions/example/handler.ts +++ b/functions/example/handler.ts @@ -1,6 +1,13 @@ -import type { FunctionHandler } from '@constructive-io/fn-runtime'; +import type { FunctionContext, FunctionHandler } from '@constructive-io/fn-runtime'; -const handler: FunctionHandler = async (params: any) => { +type ExampleParams = { + throw?: boolean; +}; + +const handler: FunctionHandler = async ( + params: ExampleParams, + _context: FunctionContext +) => { if (params.throw) { throw new Error('THROWN_ERROR'); } diff --git a/functions/export-metaschema/handler.json b/functions/export-metaschema/handler.json new file mode 100644 index 00000000..d198a009 --- /dev/null +++ b/functions/export-metaschema/handler.json @@ -0,0 +1,11 @@ +{ + "name": "export-metaschema", + "version": "1.0.0", + "type": "node-pgpm", + "description": "Exports database metaschema migrations via pgpm export", + "dependencies": { + "@constructive-io/fn-core": "workspace:^", + "@pgpmjs/core": "^6.2.0", + "pg-cache": "^3.1.0" + } +} diff --git a/functions/export-metaschema/handler.ts b/functions/export-metaschema/handler.ts new file mode 100644 index 00000000..c1062308 --- /dev/null +++ b/functions/export-metaschema/handler.ts @@ -0,0 +1,109 @@ +import type { PgpmFunctionContext, PgpmFunctionHandler } from '@constructive-io/fn-pgpm-runtime'; +import { DEFAULT_DATABASE_NAME } from '@constructive-io/fn-core'; +import { exportMigrations } from '@pgpmjs/core'; +import { getPgPool, pgCache } from 'pg-cache'; +import { resolve } from 'path'; + +type ExportMetaschemaParams = { + dbname?: string; + databaseName: string; + author?: string; + extensionName?: string; + metaExtensionName?: string; + schema_names?: string[]; + outdir?: string; + skipSchemaRenaming?: boolean; + username?: string; + repoName?: string; +}; + +const handler: PgpmFunctionHandler = async ( + params: ExportMetaschemaParams, + context: PgpmFunctionContext +) => { + const { project, options, log, env } = context; + + // Resolve database name: params > PGDATABASE env > default + const dbname = params.dbname || env.PGDATABASE || DEFAULT_DATABASE_NAME; + + log.info('[export-metaschema] Connecting to database', { dbname }); + + const pgPool = getPgPool({ database: dbname }); + + // Discover database_id from metaschema + const dbsResult = await pgPool.query( + 'SELECT id, name FROM metaschema_public.database WHERE name = $1', + [params.databaseName] + ); + + if (!dbsResult.rows.length) { + throw new Error(`Database '${params.databaseName}' not found in metaschema_public.database`); + } + + const targetRow = dbsResult.rows[0]; + + const databaseName = targetRow.name; + const database_ids = [targetRow.id]; + + // Discover schemas if not provided + let schema_names = params.schema_names; + if (!schema_names?.length) { + const schemasResult = await pgPool.query( + 'SELECT schema_name FROM metaschema_public.schema WHERE database_id = $1', + [database_ids[0]] + ); + schema_names = schemasResult.rows.map((r: any) => r.schema_name); + } + + if (!schema_names?.length) { + throw new Error(`No schemas found for database '${databaseName}'`); + } + + const author = params.author || 'Constructive '; + const extensionName = params.extensionName || databaseName; + const metaExtensionName = params.metaExtensionName || `${databaseName}-service`; + // Default username/repoName to avoid interactive prompts from scaffoldTemplate + const username = params.username || 'constructive-io'; + const repoName = params.repoName || extensionName; + + log.info('[export-metaschema] Starting export', { + dbname, + databaseName, + database_ids, + extensionName, + schema_names + }); + + project.ensureWorkspace(); + project.resetCwd(project.workspacePath); + + const outdir = params.outdir ?? resolve(project.workspacePath, 'packages/'); + + await exportMigrations({ + project, + options, + dbInfo: { + dbname, + databaseName, + database_ids + }, + author, + outdir, + schema_names, + extensionName, + metaExtensionName, + username, + repoName, + skipSchemaRenaming: params.skipSchemaRenaming + }); + + // exportMigrationsToDisk calls pgPool.end() which kills the cached pool. + // Evict the dead pool from pg-cache so the next request gets a fresh one. + pgCache.delete(dbname); + + log.info('[export-metaschema] Export complete', { outdir }); + + return { complete: true, outdir, extensionName, metaExtensionName }; +}; + +export default handler; diff --git a/functions/send-email-link/handler.ts b/functions/send-email-link/handler.ts index a2305af5..8b6988f4 100644 --- a/functions/send-email-link/handler.ts +++ b/functions/send-email-link/handler.ts @@ -1,4 +1,4 @@ -import type { FunctionHandler } from '@constructive-io/fn-runtime'; +import type { FunctionContext, FunctionHandler } from '@constructive-io/fn-runtime'; import type { GraphQLClient } from 'graphql-request'; import gql from 'graphql-tag'; import { generate } from '@launchql/mjml'; @@ -269,7 +269,10 @@ const sendEmailLink = async ( }; }; -const handler: FunctionHandler = async (params, context) => { +const handler: FunctionHandler = async ( + params: SendEmailParams, + context: FunctionContext +) => { const { client, meta, job, log, env } = context; const databaseId = job.databaseId; diff --git a/functions/simple-email/handler.ts b/functions/simple-email/handler.ts index 445a8f0c..f6954da9 100644 --- a/functions/simple-email/handler.ts +++ b/functions/simple-email/handler.ts @@ -1,4 +1,4 @@ -import type { FunctionHandler } from '@constructive-io/fn-runtime'; +import type { FunctionContext, FunctionHandler } from '@constructive-io/fn-runtime'; import { send as sendSmtp } from 'simple-smtp-server'; import { send as sendPostmaster } from '@constructive-io/postmaster'; import { parseEnvBoolean } from '@pgpmjs/env'; @@ -31,7 +31,10 @@ const isDryRun = parseEnvBoolean(process.env.SIMPLE_EMAIL_DRY_RUN) ?? false; const useSmtp = parseEnvBoolean(process.env.EMAIL_SEND_USE_SMTP) ?? false; const logger = createLogger('simple-email'); -const handler: FunctionHandler = async (params) => { +const handler: FunctionHandler = async ( + params: SimpleEmailPayload, + _context: FunctionContext +) => { const to = getRequiredField(params, 'to'); const subject = getRequiredField(params, 'subject'); diff --git a/job/service/src/index.ts b/job/service/src/index.ts index f1a8b33a..a56c65dc 100644 --- a/job/service/src/index.ts +++ b/job/service/src/index.ts @@ -39,6 +39,10 @@ const functionRegistry: Record = { 'send-email-link': { moduleName: '@constructive-io/send-email-link-fn', defaultPort: 8082 + }, + 'export-metaschema': { + moduleName: '@constructive-io/export-metaschema-fn', + defaultPort: 8083 } }; diff --git a/job/service/src/types.ts b/job/service/src/types.ts index 9732f6f3..5ed92d72 100644 --- a/job/service/src/types.ts +++ b/job/service/src/types.ts @@ -1,4 +1,4 @@ -export type FunctionName = 'simple-email' | 'send-email-link'; +export type FunctionName = 'simple-email' | 'send-email-link' | 'export-metaschema'; export type FunctionServiceConfig = { name: FunctionName; diff --git a/package.json b/package.json index 61ff7825..1c8435ff 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "access": "restricted" }, "engines": { - "node": ">=18.17.0" + "node": ">=22.0.0" }, "packageManager": "pnpm@10.12.2", "scripts": { diff --git a/packages/fn-core/package.json b/packages/fn-core/package.json new file mode 100644 index 00000000..eccf41db --- /dev/null +++ b/packages/fn-core/package.json @@ -0,0 +1,20 @@ +{ + "name": "@constructive-io/fn-core", + "version": "1.0.0", + "description": "Shared core for Constructive function runtimes — base types, server factory, and request handling", + "author": "Constructive", + "private": true, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc -p tsconfig.json", + "clean": "rimraf dist" + }, + "dependencies": { + "@constructive-io/knative-job-fn": "workspace:^" + }, + "devDependencies": { + "@types/node": "^22.10.4", + "typescript": "^5.1.6" + } +} diff --git a/packages/fn-core/src/index.ts b/packages/fn-core/src/index.ts new file mode 100644 index 00000000..f3d2c4f8 --- /dev/null +++ b/packages/fn-core/src/index.ts @@ -0,0 +1,12 @@ +export { createServer, extractHeaders } from './server'; +export type { ContextFactory } from './server'; +export type { + JobMeta, + LogFn, + Env, + BaseContext, + BaseServerOptions, + RequestHeaders, + BaseFunctionHandler +} from './types'; +export { DEFAULT_DATABASE_NAME } from './types'; diff --git a/packages/fn-core/src/server.ts b/packages/fn-core/src/server.ts new file mode 100644 index 00000000..78be6d82 --- /dev/null +++ b/packages/fn-core/src/server.ts @@ -0,0 +1,36 @@ +import { createJobApp } from '@constructive-io/knative-job-fn'; +import type { BaseContext, BaseFunctionHandler, RequestHeaders } from './types'; + +export type ContextFactory = ( + headers: RequestHeaders +) => C | Promise; + +export const extractHeaders = (req: any): RequestHeaders => ({ + databaseId: + req.get('X-Database-Id') || + req.get('x-database-id') || + process.env.DEFAULT_DATABASE_ID, + workerId: req.get('X-Worker-Id') || req.get('x-worker-id'), + jobId: req.get('X-Job-Id') || req.get('x-job-id') +}); + +export const createServer = ( + handler: BaseFunctionHandler, + contextFactory: ContextFactory +) => { + const app = createJobApp(); + + app.post('/', async (req: any, res: any, next: any) => { + try { + const headers = extractHeaders(req); + const context = await contextFactory(headers); + const params = req.body || {}; + const result = await handler(params, context); + res.status(200).json(result); + } catch (err) { + next(err); + } + }); + + return app; +}; diff --git a/packages/fn-core/src/types.ts b/packages/fn-core/src/types.ts new file mode 100644 index 00000000..0a056beb --- /dev/null +++ b/packages/fn-core/src/types.ts @@ -0,0 +1,37 @@ +export type JobMeta = { + jobId?: string; + workerId?: string; + databaseId?: string; +}; + +export type LogFn = { + info: (...args: any[]) => void; + error: (...args: any[]) => void; + warn: (...args: any[]) => void; +}; + +export type Env = Record; + +export type BaseContext = { + job: JobMeta; + log: LogFn; + env: Env; +}; + +export type BaseServerOptions = { + name?: string; +}; + +export type RequestHeaders = { + databaseId?: string; + workerId?: string; + jobId?: string; +}; + +export type BaseFunctionHandler< + P = unknown, + C extends BaseContext = BaseContext, + R = unknown +> = (params: P, context: C) => Promise | R; + +export const DEFAULT_DATABASE_NAME = 'constructive'; diff --git a/packages/fn-core/tsconfig.json b/packages/fn-core/tsconfig.json new file mode 100644 index 00000000..a45c5275 --- /dev/null +++ b/packages/fn-core/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "declaration": true + }, + "include": ["src/**/*.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/packages/fn-pgpm-runtime/package.json b/packages/fn-pgpm-runtime/package.json new file mode 100644 index 00000000..8772a831 --- /dev/null +++ b/packages/fn-pgpm-runtime/package.json @@ -0,0 +1,24 @@ +{ + "name": "@constructive-io/fn-pgpm-runtime", + "version": "1.0.0", + "description": "Runtime for pgpm-based Constructive functions — wraps handler in Express app with PgpmPackage, env options, and job callback support", + "author": "Constructive", + "private": true, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc -p tsconfig.json", + "clean": "rimraf dist" + }, + "dependencies": { + "@constructive-io/fn-core": "workspace:^", + "@pgpmjs/core": "^6.2.0", + "@pgpmjs/env": "^2.11.0", + "@pgpmjs/logger": "^2.1.0", + "@pgpmjs/types": "^2.17.0" + }, + "devDependencies": { + "@types/node": "^22.10.4", + "typescript": "^5.1.6" + } +} diff --git a/packages/fn-pgpm-runtime/src/context.ts b/packages/fn-pgpm-runtime/src/context.ts new file mode 100644 index 00000000..90e0de48 --- /dev/null +++ b/packages/fn-pgpm-runtime/src/context.ts @@ -0,0 +1,26 @@ +import type { Env, LogFn, RequestHeaders } from '@constructive-io/fn-core'; +import type { PgpmPackage } from '@pgpmjs/core'; +import type { PgpmOptions } from '@pgpmjs/types'; +import type { PgpmFunctionContext } from './types'; + +export type PgpmServerResources = { + project: PgpmPackage; + options: PgpmOptions; + log: LogFn; + env: Env; +}; + +export const buildPgpmContext = ( + headers: RequestHeaders, + resources: PgpmServerResources +): PgpmFunctionContext => ({ + job: { + jobId: headers.jobId, + workerId: headers.workerId, + databaseId: headers.databaseId + }, + project: resources.project, + options: resources.options, + log: resources.log, + env: resources.env +}); diff --git a/packages/fn-pgpm-runtime/src/index.ts b/packages/fn-pgpm-runtime/src/index.ts new file mode 100644 index 00000000..1883de6a --- /dev/null +++ b/packages/fn-pgpm-runtime/src/index.ts @@ -0,0 +1,4 @@ +export { createPgpmFunctionServer } from './server'; +export { buildPgpmContext } from './context'; +export type { PgpmServerResources } from './context'; +export type { PgpmFunctionHandler, PgpmFunctionContext, PgpmServerOptions } from './types'; diff --git a/packages/fn-pgpm-runtime/src/server.ts b/packages/fn-pgpm-runtime/src/server.ts new file mode 100644 index 00000000..2352c46a --- /dev/null +++ b/packages/fn-pgpm-runtime/src/server.ts @@ -0,0 +1,25 @@ +import { createServer } from '@constructive-io/fn-core'; +import type { Env, RequestHeaders } from '@constructive-io/fn-core'; +import { PgpmPackage } from '@pgpmjs/core'; +import { getEnvOptions } from '@pgpmjs/env'; +import { createLogger } from '@pgpmjs/logger'; +import { buildPgpmContext } from './context'; +import type { PgpmFunctionHandler, PgpmServerOptions } from './types'; + +export const createPgpmFunctionServer = ( + handler: PgpmFunctionHandler, + options: PgpmServerOptions = {} +) => { + // Initialize shared resources once at server startup (not per-request) + const env = process.env as Env; + const cwd = options.cwd || env.PGPM_CWD || process.cwd(); + const project = new PgpmPackage(cwd); + const pgpmOptions = getEnvOptions(); + const log = createLogger(options.name || 'fn-pgpm'); + + const resources = { project, options: pgpmOptions, log, env }; + + return createServer(handler, (headers: RequestHeaders) => + buildPgpmContext(headers, resources) + ); +}; diff --git a/packages/fn-pgpm-runtime/src/types.ts b/packages/fn-pgpm-runtime/src/types.ts new file mode 100644 index 00000000..b45762ef --- /dev/null +++ b/packages/fn-pgpm-runtime/src/types.ts @@ -0,0 +1,14 @@ +import type { PgpmPackage } from '@pgpmjs/core'; +import type { PgpmOptions } from '@pgpmjs/types'; +import type { BaseContext, BaseFunctionHandler, BaseServerOptions } from '@constructive-io/fn-core'; + +export type PgpmFunctionContext = BaseContext & { + project: PgpmPackage; + options: PgpmOptions; +}; + +export type PgpmFunctionHandler

= BaseFunctionHandler; + +export type PgpmServerOptions = BaseServerOptions & { + cwd?: string; +}; diff --git a/packages/fn-pgpm-runtime/tsconfig.json b/packages/fn-pgpm-runtime/tsconfig.json new file mode 100644 index 00000000..b2c6b8e8 --- /dev/null +++ b/packages/fn-pgpm-runtime/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "declaration": true, + "declarationMap": true + }, + "include": ["src"], + "exclude": ["dist", "node_modules"] +} diff --git a/packages/fn-runtime/package.json b/packages/fn-runtime/package.json index cef31b26..a81175ef 100644 --- a/packages/fn-runtime/package.json +++ b/packages/fn-runtime/package.json @@ -11,7 +11,7 @@ "clean": "rimraf dist" }, "dependencies": { - "@constructive-io/knative-job-fn": "workspace:^", + "@constructive-io/fn-core": "workspace:^", "@pgpmjs/logger": "^2.1.0", "graphql-request": "^7.1.2" }, diff --git a/packages/fn-runtime/src/context.ts b/packages/fn-runtime/src/context.ts index 810665d4..d8613faa 100644 --- a/packages/fn-runtime/src/context.ts +++ b/packages/fn-runtime/src/context.ts @@ -1,16 +1,11 @@ +import type { RequestHeaders } from '@constructive-io/fn-core'; import { createLogger } from '@pgpmjs/logger'; import { createClients } from './graphql'; -import type { FunctionContext } from './types'; - -type RequestHeaders = { - databaseId?: string; - workerId?: string; - jobId?: string; -}; +import type { FunctionContext, ServerOptions } from './types'; export const buildContext = ( headers: RequestHeaders, - options: { name?: string } = {} + options: ServerOptions = {} ): FunctionContext => { const env = process.env as Record; const log = createLogger(options.name || 'fn-runtime'); diff --git a/packages/fn-runtime/src/server.ts b/packages/fn-runtime/src/server.ts index c559a790..8a10cc10 100644 --- a/packages/fn-runtime/src/server.ts +++ b/packages/fn-runtime/src/server.ts @@ -1,4 +1,5 @@ -import { createJobApp } from '@constructive-io/knative-job-fn'; +import { createServer } from '@constructive-io/fn-core'; +import type { RequestHeaders } from '@constructive-io/fn-core'; import { buildContext } from './context'; import type { FunctionHandler, ServerOptions } from './types'; @@ -6,27 +7,7 @@ export const createFunctionServer = ( handler: FunctionHandler, options: ServerOptions = {} ) => { - const app = createJobApp(); - - app.post('/', async (req: any, res: any, next: any) => { - try { - const context = buildContext( - { - databaseId: req.get('X-Database-Id') || req.get('x-database-id') || process.env.DEFAULT_DATABASE_ID, - workerId: req.get('X-Worker-Id') || req.get('x-worker-id'), - jobId: req.get('X-Job-Id') || req.get('x-job-id') - }, - { name: options.name } - ); - - const params = req.body || {}; - const result = await handler(params, context); - - res.status(200).json(result); - } catch (err) { - next(err); - } - }); - - return app; + return createServer(handler, (headers: RequestHeaders) => + buildContext(headers, options) + ); }; diff --git a/packages/fn-runtime/src/types.ts b/packages/fn-runtime/src/types.ts index 033a70c5..f74513e7 100644 --- a/packages/fn-runtime/src/types.ts +++ b/packages/fn-runtime/src/types.ts @@ -1,22 +1,11 @@ import type { GraphQLClient } from 'graphql-request'; +import type { BaseContext, BaseFunctionHandler, BaseServerOptions } from '@constructive-io/fn-core'; -export type FunctionHandler

= ( - params: P, - context: FunctionContext -) => Promise | R; - -export type FunctionContext = { - job: { - jobId?: string; - workerId?: string; - databaseId?: string; - }; +export type FunctionContext = BaseContext & { client: GraphQLClient; meta: GraphQLClient; - log: { info: (...args: any[]) => void; error: (...args: any[]) => void; warn: (...args: any[]) => void }; - env: Record; }; -export type ServerOptions = { - name?: string; -}; +export type FunctionHandler

= BaseFunctionHandler; + +export type ServerOptions = BaseServerOptions; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7df5653c..1f0da0b9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -64,6 +64,31 @@ importers: specifier: ^5.1.6 version: 5.9.3 + generated/export-metaschema: + dependencies: + '@constructive-io/fn-core': + specifier: workspace:^ + version: link:../../packages/fn-core + '@constructive-io/fn-pgpm-runtime': + specifier: workspace:^ + version: link:../../packages/fn-pgpm-runtime + '@pgpmjs/core': + specifier: ^6.2.0 + version: 6.3.0 + pg-cache: + specifier: ^3.1.0 + version: 3.1.0 + devDependencies: + '@types/node': + specifier: ^22.10.4 + version: 22.19.3 + makage: + specifier: ^0.1.10 + version: 0.1.12 + typescript: + specifier: ^5.1.6 + version: 5.9.3 + generated/send-email-link: dependencies: '@constructive-io/fn-runtime': @@ -292,11 +317,49 @@ importers: specifier: ^5.1.6 version: 5.9.3 - packages/fn-runtime: + packages/fn-core: dependencies: '@constructive-io/knative-job-fn': specifier: workspace:^ version: link:../fn-app + devDependencies: + '@types/node': + specifier: ^22.10.4 + version: 22.19.3 + typescript: + specifier: ^5.1.6 + version: 5.9.3 + + packages/fn-pgpm-runtime: + dependencies: + '@constructive-io/fn-core': + specifier: workspace:^ + version: link:../fn-core + '@pgpmjs/core': + specifier: ^6.2.0 + version: 6.3.0 + '@pgpmjs/env': + specifier: ^2.11.0 + version: 2.13.0 + '@pgpmjs/logger': + specifier: ^2.1.0 + version: 2.2.0 + '@pgpmjs/types': + specifier: ^2.17.0 + version: 2.17.0 + devDependencies: + '@types/node': + specifier: ^22.10.4 + version: 22.19.3 + typescript: + specifier: ^5.1.6 + version: 5.9.3 + + packages/fn-runtime: + dependencies: + '@constructive-io/fn-core': + specifier: workspace:^ + version: link:../fn-core '@pgpmjs/logger': specifier: ^2.1.0 version: 2.1.0 @@ -725,15 +788,36 @@ packages: '@one-ini/wasm@0.1.1': resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + '@pgpmjs/core@6.3.0': + resolution: {integrity: sha512-Cfg/pOwgL5gWcOlNDaKefyXf97omhyKgIC7K44Wd37WH96Uy75nFD39fYdznkCdTQB0nieCfAHpEFuQpLC9JnQ==} + '@pgpmjs/env@2.11.0': resolution: {integrity: sha512-2UHzFEsYY41d9BEDLAO2SoqUXLZaaI3c4pb3ZXdRQWYss28nQ8f3XPYe5V78sefi8PEM4Lj2G/6ujZgO4lwZQg==} + '@pgpmjs/env@2.13.0': + resolution: {integrity: sha512-b7RDvkfWlhiqsQm8YZ9lfDUqvVjlZ7GBF7jhj+194OuAmNMELp1IdiKbIGmgu125BTr4ucNd0HMGmhNE3Sjmsw==} + '@pgpmjs/logger@2.1.0': resolution: {integrity: sha512-AQHt6BMnb+0iv8MXmb9kuQfe7/PDBqkkiIaGtzV6WFH4i0oNB43rQBwX4oKc6cSGl6bRXH03o4KFutS3duWCaA==} + '@pgpmjs/logger@2.2.0': + resolution: {integrity: sha512-dNfgUiMWzbYYDub0Kg6ERZjRZY+QT8JK5GBNrP8msWjSYoX8GLW5rtcecyF7BuPPrEACM37mol9yYBaSuFZJsQ==} + + '@pgpmjs/server-utils@3.2.0': + resolution: {integrity: sha512-xDQW89aI5KCldrRIsCF+IXxHiw2AvtImnQjBDNNHr+M9Dp73T22CkbTg3nERpvhRKkNu3VKPegkGaBLDCFqtPw==} + '@pgpmjs/types@2.16.0': resolution: {integrity: sha512-be/RIFg2TYB2X9LAVZ4mFkhu3ZZMpzBCBR9umvQUDEfMcb7aUYDFdEw+mc7CHBgifXNliUswXmllZVsrurh6TQ==} + '@pgpmjs/types@2.17.0': + resolution: {integrity: sha512-qMIi67ZNWkzV/oOWf9BvR3aat2hNLCqsCP4YkMOcPYi42WsIsch9mev3K+jRPkTPKrUAVkkEAnTbnCx3vW8y7w==} + + '@pgsql/types@17.6.2': + resolution: {integrity: sha512-1UtbELdbqNdyOShhrVfSz3a1gDi0s9XXiQemx+6QqtsrXe62a6zOGU+vjb2GRfG5jeEokI1zBBcfD42enRv0Rw==} + + '@pgsql/utils@17.8.12': + resolution: {integrity: sha512-J9WZUgHAZdzG5klUSKNoTFv4gH1l/bkKX1vEboz/EjvEVcoIVTE3hI4lFkhfDkVGscDzTRSM6lZuD/AfQhlAOA==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -1090,6 +1174,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + appstash@0.5.0: + resolution: {integrity: sha512-f9CkbNq1UK2aRn7ErcZI4C1ojInalknp+GsjHnlGSM35sKDBYf6lDc3Z6hViH751hOI0tSrNcFunkaYvxWYgKQ==} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -1154,6 +1241,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + base-64@1.0.0: resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} @@ -1185,6 +1276,10 @@ packages: brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@5.0.3: + resolution: {integrity: sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==} + engines: {node: 18 || 20 || >=22} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -1353,6 +1448,10 @@ packages: core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + cron-parser@2.18.0: resolution: {integrity: sha512-s4odpheTyydAbTBQepsqd2rNWGa2iV3cyo8g7zbI2QQYGLVsfbhmwukayS1XHppe02Oy1fg7mg6xoaraVJeEcg==} engines: {node: '>=0.8'} @@ -1381,6 +1480,15 @@ packages: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} + csv-parser@2.3.5: + resolution: {integrity: sha512-LCHolC4AlNwL+5EuD5LH2VVNKpD8QixZW2zzK1XmrVYUaslFY4c5BooERHOCIubG9iv/DAyFjs4x0HvWNZuyWg==} + engines: {node: '>= 8.16.0'} + hasBin: true + + csv-to-pg@3.8.0: + resolution: {integrity: sha512-suSA2GSd3TFrKCvuIQ2fE8PsQRTfxglFJP68V2vqNmM/LiBG2UUs0sxmx0+MPk8ooYX/BTX5fM1g2jQSp0j86g==} + hasBin: true + dashdash@1.14.1: resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} engines: {node: '>=0.10'} @@ -1711,6 +1819,9 @@ packages: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} + find-and-require-package-json@0.9.1: + resolution: {integrity: sha512-jFpCL0XgjipSk109viUtfp+NyR/oW6a4Xus4tV3UYkmCbsjisEeZD1x5QnD1NDDK/hXas1WFs4yO13L4TPXWlQ==} + find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -1773,6 +1884,9 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + genomic@5.3.4: + resolution: {integrity: sha512-SmevgRHHaq7rt85k113zEWchUGZRbfk4loppkCK/O73l6PG3IVEbX2qwmeZZaOpS4WudTGkKhH/emZPx9M4pcg==} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -1818,6 +1932,10 @@ packages: deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me @@ -1975,6 +2093,9 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + inquirerer@4.5.1: + resolution: {integrity: sha512-/Cis0BNeqdgcXJt3loHKt7PbfawPG7fLTQHr29IfpOHCRaLACmf5737PAHakVU1rBflCNNMo4lpdso6t4FHpjg==} + ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -2244,6 +2365,9 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + komoji@0.8.1: + resolution: {integrity: sha512-7wYXVGaHc+MNTyOoOVmgXA08bRXWm5TDoRdQuLCBFnQsR7TGf+q1bth1E8caIHJit0sbYCTeBAdk3QHxnpYzYQ==} + leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -2252,6 +2376,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + libpg-query@17.7.3: + resolution: {integrity: sha512-lHKBvoWRsXt/9bJxpAeFxkLu0CA6tELusqy3o1z6/DwGXSETxhKJDaNlNdrNV8msvXDLBhpg/4RE/fKKs5rYFA==} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -2365,6 +2492,10 @@ packages: resolution: {integrity: sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==} engines: {node: 20 || >=22} + minimatch@10.2.4: + resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} + engines: {node: 18 || 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -2383,6 +2514,10 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + mjml-accordion@4.7.1: resolution: {integrity: sha512-oYwC/CLOUWJ6pRt2saDHj/HytGOHO5B5lKNqUAhKPye5HFNZykKEV5ChmZ2NfGsGU+9BhQ7H5DaCafp4fDmPAg==} @@ -2513,6 +2648,9 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + nested-obj@0.1.5: + resolution: {integrity: sha512-04Y7qDMlI8RbYTn0cJAKaw/mLrO9UmLj3xbrjTZKDfOn9f3b/RXEQFIIpveJlwn8KfPwdVFWLZUaL5gNuQ7G0w==} + no-case@2.3.2: resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==} @@ -2625,6 +2763,9 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-package-name@1.0.0: + resolution: {integrity: sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==} + parse5-htmlparser2-tree-adapter@7.1.0: resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} @@ -2661,6 +2802,10 @@ packages: resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} engines: {node: 20 || >=22} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + path-to-regexp@8.3.0: resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} @@ -2670,6 +2815,9 @@ packages: pg-cache@2.1.0: resolution: {integrity: sha512-r3cMPc62l2EHZwbCPS20X0gJPp/wjz66wknN38eiTYzQE7CShXHGAKbS96xDvWxVAcDGEDhiRJrx5eV1Qu+sUA==} + pg-cache@3.1.0: + resolution: {integrity: sha512-mOzEIVlWgqtZ+rcRFki0OVlURyRqTAzL8q5KH+2/Zu0F6DzmP+ejGLU4lKy4MYW0mjgSUJSIg60bPmGpLG34rQ==} + pg-cloudflare@1.3.0: resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} @@ -2679,6 +2827,9 @@ packages: pg-env@1.4.0: resolution: {integrity: sha512-Xl56AT5Gs/38ubNXSekW02n9USfA+UkIrsl/T0jhES/oKLQccsWPYm+tPeXHc0asdwnFhWxoqbDr2K1vvMv5mA==} + pg-env@1.5.0: + resolution: {integrity: sha512-VHtDiIj5ha8+m0WowxOPuKfPqm4srt+/VOFhFdyqXwSpsXu0TKFmkWrmzsypveUXtsASVlCFa7MDWSgezCyExQ==} + pg-int8@1.0.1: resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} engines: {node: '>=4.0.0'} @@ -2707,6 +2858,12 @@ packages: pgpass@1.0.5: resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + pgsql-deparser@17.17.2: + resolution: {integrity: sha512-FCjqKY3Sdmce3VUd3CxCXF0kqaZ0s4a6yIMT5UJ9vETh0cF54A8Tpqjn0qBKaPUD8xqTKeLdS+SfiwjAC64wrA==} + + pgsql-parser@17.9.11: + resolution: {integrity: sha512-Bqp9uLvJK0Qht9PXzI6eC/Fn+lFRL+2eMvXss4D4qt7lxPLIHS8FMKYOHUQNTI3m6ylExSOdNXhx/DL5UGm3xg==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -3050,6 +3207,9 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} + through2@3.0.2: + resolution: {integrity: sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -3535,7 +3695,7 @@ snapshots: '@constructive-io/job-utils@1.1.0': dependencies: - '@pgpmjs/env': 2.11.0 + '@pgpmjs/env': 2.13.0 '@pgpmjs/logger': 2.1.0 '@pgpmjs/types': 2.16.0 pg-cache: 2.1.0 @@ -3899,19 +4059,71 @@ snapshots: '@one-ini/wasm@0.1.1': {} + '@pgpmjs/core@6.3.0': + dependencies: + '@pgpmjs/env': 2.13.0 + '@pgpmjs/logger': 2.2.0 + '@pgpmjs/server-utils': 3.2.0 + '@pgpmjs/types': 2.17.0 + csv-to-pg: 3.8.0 + genomic: 5.3.4 + glob: 13.0.6 + komoji: 0.8.1 + minimatch: 10.2.4 + parse-package-name: 1.0.0 + pg: 8.17.1 + pg-cache: 3.1.0 + pg-env: 1.5.0 + pgsql-deparser: 17.17.2 + pgsql-parser: 17.9.11 + yanse: 0.2.1 + transitivePeerDependencies: + - pg-native + - supports-color + '@pgpmjs/env@2.11.0': dependencies: '@pgpmjs/types': 2.16.0 deepmerge: 4.3.1 + '@pgpmjs/env@2.13.0': + dependencies: + '@pgpmjs/types': 2.17.0 + deepmerge: 4.3.1 + '@pgpmjs/logger@2.1.0': dependencies: yanse: 0.2.1 + '@pgpmjs/logger@2.2.0': + dependencies: + yanse: 0.2.1 + + '@pgpmjs/server-utils@3.2.0': + dependencies: + '@pgpmjs/logger': 2.2.0 + '@pgpmjs/types': 2.17.0 + cors: 2.8.6 + express: 5.2.1 + lru-cache: 11.2.5 + transitivePeerDependencies: + - supports-color + '@pgpmjs/types@2.16.0': dependencies: pg-env: 1.4.0 + '@pgpmjs/types@2.17.0': + dependencies: + pg-env: 1.5.0 + + '@pgsql/types@17.6.2': {} + + '@pgsql/utils@17.8.12': + dependencies: + '@pgsql/types': 17.6.2 + nested-obj: 0.1.5 + '@pkgjs/parseargs@0.11.0': optional: true @@ -4290,6 +4502,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + appstash@0.5.0: {} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -4391,6 +4605,8 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + base-64@1.0.0: {} baseline-browser-mapping@2.9.11: {} @@ -4441,6 +4657,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.3: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -4615,6 +4835,11 @@ snapshots: core-util-is@1.0.2: {} + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cron-parser@2.18.0: dependencies: is-nan: 1.3.2 @@ -4653,6 +4878,20 @@ snapshots: css-what@6.2.2: {} + csv-parser@2.3.5: + dependencies: + minimist: 1.2.8 + through2: 3.0.2 + + csv-to-pg@3.8.0: + dependencies: + '@pgsql/types': 17.6.2 + '@pgsql/utils': 17.8.12 + csv-parser: 2.3.5 + inquirerer: 4.5.1 + js-yaml: 3.14.2 + pgsql-deparser: 17.17.2 + dashdash@1.14.1: dependencies: assert-plus: 1.0.0 @@ -5014,6 +5253,8 @@ snapshots: transitivePeerDependencies: - supports-color + find-and-require-package-json@0.9.1: {} + find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -5074,6 +5315,11 @@ snapshots: function-bind@1.1.2: {} + genomic@5.3.4: + dependencies: + appstash: 0.5.0 + inquirerer: 4.5.1 + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} @@ -5130,6 +5376,12 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 2.0.1 + glob@13.0.6: + dependencies: + minimatch: 10.2.4 + minipass: 7.1.3 + path-scurry: 2.0.2 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -5297,6 +5549,13 @@ snapshots: ini@1.3.8: {} + inquirerer@4.5.1: + dependencies: + deepmerge: 4.3.1 + find-and-require-package-json: 0.9.1 + minimist: 1.2.8 + yanse: 0.2.1 + ipaddr.js@1.9.1: {} is-arrayish@0.2.1: {} @@ -5744,6 +6003,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + komoji@0.8.1: {} + leven@3.1.0: {} levn@0.4.1: @@ -5751,6 +6012,10 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + libpg-query@17.7.3: + dependencies: + '@pgsql/types': 17.6.2 + lines-and-columns@1.2.4: {} locate-path@5.0.0: @@ -5843,6 +6108,10 @@ snapshots: dependencies: '@isaacs/brace-expansion': 5.0.1 + minimatch@10.2.4: + dependencies: + brace-expansion: 5.0.3 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -5859,6 +6128,8 @@ snapshots: minipass@7.1.2: {} + minipass@7.1.3: {} + mjml-accordion@4.7.1: dependencies: '@babel/runtime': 7.28.4 @@ -6170,6 +6441,8 @@ snapshots: neo-async@2.6.2: {} + nested-obj@0.1.5: {} + no-case@2.3.2: dependencies: lower-case: 1.1.4 @@ -6276,6 +6549,8 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-package-name@1.0.0: {} + parse5-htmlparser2-tree-adapter@7.1.0: dependencies: domhandler: 5.0.3 @@ -6311,20 +6586,35 @@ snapshots: lru-cache: 11.2.5 minipass: 7.1.2 + path-scurry@2.0.2: + dependencies: + lru-cache: 11.2.5 + minipass: 7.1.3 + path-to-regexp@8.3.0: {} performance-now@2.1.0: {} pg-cache@2.1.0: dependencies: - '@pgpmjs/logger': 2.1.0 - '@pgpmjs/types': 2.16.0 + '@pgpmjs/logger': 2.2.0 + '@pgpmjs/types': 2.17.0 lru-cache: 11.2.5 pg: 8.17.1 pg-env: 1.4.0 transitivePeerDependencies: - pg-native + pg-cache@3.1.0: + dependencies: + '@pgpmjs/logger': 2.2.0 + '@pgpmjs/types': 2.17.0 + lru-cache: 11.2.5 + pg: 8.17.1 + pg-env: 1.5.0 + transitivePeerDependencies: + - pg-native + pg-cloudflare@1.3.0: optional: true @@ -6332,6 +6622,8 @@ snapshots: pg-env@1.4.0: {} + pg-env@1.5.0: {} + pg-int8@1.0.1: {} pg-pool@3.11.0(pg@8.17.1): @@ -6362,6 +6654,16 @@ snapshots: dependencies: split2: 4.2.0 + pgsql-deparser@17.17.2: + dependencies: + '@pgsql/types': 17.6.2 + + pgsql-parser@17.9.11: + dependencies: + '@pgsql/types': 17.6.2 + libpg-query: 17.7.3 + pgsql-deparser: 17.17.2 + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -6617,7 +6919,7 @@ snapshots: simple-smtp-server@0.3.0: dependencies: - '@pgpmjs/env': 2.11.0 + '@pgpmjs/env': 2.13.0 '@pgpmjs/types': 2.16.0 nodemailer: 6.10.1 @@ -6749,6 +7051,11 @@ snapshots: glob: 7.2.3 minimatch: 3.1.2 + through2@3.0.2: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) diff --git a/templates/node-pgpm/Dockerfile b/templates/node-pgpm/Dockerfile new file mode 100644 index 00000000..52c9fc71 --- /dev/null +++ b/templates/node-pgpm/Dockerfile @@ -0,0 +1,20 @@ +FROM node:22-alpine AS build +RUN npm install -g pnpm@10.12.2 +WORKDIR /app +COPY . . +RUN node --experimental-strip-types scripts/generate.ts \ + && pnpm install --frozen-lockfile \ + && pnpm --filter @constructive-io/{{name}}-fn... build + +FROM node:22-alpine AS deploy +RUN npm install -g pnpm@10.12.2 +COPY --from=build /app /app +WORKDIR /app +RUN pnpm --filter @constructive-io/{{name}}-fn deploy --legacy /deploy --prod + +FROM node:22-alpine +WORKDIR /app +COPY --from=deploy /deploy . +ENV NODE_ENV=production +EXPOSE 8080 +CMD ["node", "dist/index.js"] diff --git a/templates/node-pgpm/README.md b/templates/node-pgpm/README.md new file mode 100644 index 00000000..d03daa07 --- /dev/null +++ b/templates/node-pgpm/README.md @@ -0,0 +1,3 @@ +# {{name}}-fn + +{{description}} diff --git a/templates/node-pgpm/index.ts b/templates/node-pgpm/index.ts new file mode 100644 index 00000000..35807ed7 --- /dev/null +++ b/templates/node-pgpm/index.ts @@ -0,0 +1,10 @@ +import { createPgpmFunctionServer } from '@constructive-io/fn-pgpm-runtime'; +import handler from './handler'; + +const app = createPgpmFunctionServer(handler, { name: '{{name}}' }); + +export default app; + +if (require.main === module) { + app.listen(Number(process.env.PORT || 8080)); +} diff --git a/templates/node-pgpm/k8s/knative-service.yaml b/templates/node-pgpm/k8s/knative-service.yaml new file mode 100644 index 00000000..b2cf2ec6 --- /dev/null +++ b/templates/node-pgpm/k8s/knative-service.yaml @@ -0,0 +1,72 @@ +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + name: {{name}} + labels: + app.kubernetes.io/name: {{name}} + app.kubernetes.io/component: function + app.kubernetes.io/part-of: constructive-jobs + networking.knative.dev/visibility: cluster-local +spec: + template: + metadata: + labels: + app.kubernetes.io/name: {{name}} + app.kubernetes.io/component: function + app.kubernetes.io/part-of: constructive-jobs + annotations: + autoscaling.knative.dev/minScale: "1" + autoscaling.knative.dev/maxScale: "10" + autoscaling.knative.dev/target: "50" + spec: + containerConcurrency: 10 + timeoutSeconds: 300 + containers: + - name: function + image: ghcr.io/constructive-io/{{name}}-fn:latest + imagePullPolicy: Always + ports: + - containerPort: 8080 + protocol: TCP + env: + - name: NODE_ENV + value: "production" + - name: PORT + value: "8080" + - name: PGHOST + valueFrom: + secretKeyRef: + name: constructive-db-credentials + key: host + - name: PGPORT + valueFrom: + secretKeyRef: + name: constructive-db-credentials + key: port + optional: true + - name: PGUSER + valueFrom: + secretKeyRef: + name: constructive-db-credentials + key: user + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: constructive-db-credentials + key: password + - name: PGDATABASE + valueFrom: + secretKeyRef: + name: constructive-db-credentials + key: database + optional: true + resources: + requests: + memory: "256Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1000m" + traffic: + - percent: 100 + latestRevision: true diff --git a/templates/node-pgpm/package.json b/templates/node-pgpm/package.json new file mode 100644 index 00000000..73309198 --- /dev/null +++ b/templates/node-pgpm/package.json @@ -0,0 +1,21 @@ +{ + "name": "@constructive-io/{{name}}-fn", + "version": "{{version}}", + "description": "{{description}}", + "private": true, + "main": "dist/index.js", + "scripts": { + "build": "makage build", + "build:dev": "makage build --dev", + "clean": "makage clean", + "lint": "eslint . --fix" + }, + "dependencies": { + "@constructive-io/fn-pgpm-runtime": "workspace:^" + }, + "devDependencies": { + "@types/node": "^22.10.4", + "makage": "^0.1.10", + "typescript": "^5.1.6" + } +} diff --git a/templates/node-pgpm/tsconfig.esm.json b/templates/node-pgpm/tsconfig.esm.json new file mode 100644 index 00000000..796ea74b --- /dev/null +++ b/templates/node-pgpm/tsconfig.esm.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "dist/esm", + "module": "es2022", + "rootDir": ".", + "declaration": false + } +} diff --git a/templates/node-pgpm/tsconfig.json b/templates/node-pgpm/tsconfig.json new file mode 100644 index 00000000..6a244e75 --- /dev/null +++ b/templates/node-pgpm/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "." + }, + "include": [ + "index.ts", + "handler.ts" + ], + "exclude": [ + "dist", + "node_modules" + ] +}