diff --git a/functions/example/handler.ts b/functions/example/handler.ts index dc83dc2..fd9c555 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 0000000..d198a00 --- /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 0000000..c106230 --- /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 a2305af..8b6988f 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 445a8f0..f6954da 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 f1a8b33..a56c65d 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 9732f6f..5ed92d7 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 61ff782..1c8435f 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 0000000..eccf41d --- /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 0000000..f3d2c4f --- /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 0000000..78be6d8 --- /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 0000000..0a056be --- /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 0000000..a45c527 --- /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 0000000..8772a83 --- /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 0000000..90e0de4 --- /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 0000000..1883de6 --- /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 0000000..2352c46 --- /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 0000000..b45762e --- /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 0000000..b2c6b8e --- /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 cef31b2..a81175e 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 810665d..d8613fa 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 c559a79..8a10cc1 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 033a70c..f74513e 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 7df5653..1f0da0b 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 0000000..52c9fc7 --- /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 0000000..d03daa0 --- /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 0000000..35807ed --- /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 0000000..b2cf2ec --- /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 0000000..7330919 --- /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 0000000..796ea74 --- /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 0000000..6a244e7 --- /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" + ] +}