@invect/express 0.0.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/invect-router.ts"],"sourcesContent":["import { Router } from 'express';\nimport type { Request, Response, NextFunction } from 'express';\nimport {\n BatchProvider,\n Invect,\n InvectConfig,\n GraphNodeType,\n InvectIdentity,\n InvectPermission,\n InvectResourceType,\n} from '@invect/core';\nimport type { CredentialFilters } from '@invect/core';\nimport { asyncHandler } from './async-handler';\nimport { ZodError } from 'zod';\n\ntype PostgreSqlClientLike = {\n unsafe<T = Record<string, unknown>>(statement: string, params?: unknown[]): Promise<T[]>;\n};\n\ntype SqliteClientLike = {\n prepare(sql: string): {\n all(...params: unknown[]): Record<string, unknown>[];\n run(...params: unknown[]): { changes: number };\n };\n};\n\ntype MysqlClientLike = {\n execute<T = Record<string, unknown>>(\n statement: string,\n params: unknown[],\n ): Promise<[T[] | unknown, unknown]>;\n};\n\nfunction createPluginDatabaseApi(core: Invect) {\n const connection = core.getDatabaseConnection();\n\n const normalizeSql = (statement: string): string => {\n if (connection.type !== 'postgresql') {\n return statement;\n }\n\n let index = 0;\n return statement.replace(/\\?/g, () => `$${++index}`);\n };\n\n const query = async <T = Record<string, unknown>>(\n statement: string,\n params: unknown[] = [],\n ): Promise<T[]> => {\n switch (connection.type) {\n case 'postgresql': {\n const client = (connection.db as unknown as { $client: PostgreSqlClientLike }).$client;\n return await client.unsafe<T>(normalizeSql(statement), params);\n }\n case 'sqlite': {\n const client = (connection.db as unknown as { $client: SqliteClientLike }).$client;\n return client.prepare(statement).all(...params) as T[];\n }\n case 'mysql': {\n const client = (connection.db as unknown as { $client: MysqlClientLike }).$client;\n const [rows] = await client.execute<T>(statement, params);\n return Array.isArray(rows) ? rows : [];\n }\n }\n\n throw new Error(`Unsupported database type: ${String((connection as { type?: string }).type)}`);\n };\n\n return {\n type: connection.type,\n query,\n async execute(statement: string, params: unknown[] = []): Promise<void> {\n switch (connection.type) {\n case 'postgresql': {\n const client = (connection.db as unknown as { $client: PostgreSqlClientLike }).$client;\n await client.unsafe(normalizeSql(statement), params);\n return;\n }\n case 'sqlite': {\n const client = (connection.db as unknown as { $client: SqliteClientLike }).$client;\n // better-sqlite3 doesn't accept booleans — coerce to 0/1\n const coerced = params.map((p) => (typeof p === 'boolean' ? (p ? 1 : 0) : p));\n client.prepare(statement).run(...coerced);\n return;\n }\n case 'mysql': {\n const client = (connection.db as unknown as { $client: MysqlClientLike }).$client;\n await client.execute(statement, params);\n return;\n }\n }\n throw new Error(\n `Unsupported database type: ${String((connection as { type?: string }).type)}`,\n );\n },\n };\n}\n\n// Extend Express Request type to include Invect identity\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Express {\n interface Request {\n /** Invect identity resolved from host app auth */\n invectIdentity?: InvectIdentity | null;\n }\n }\n}\n\nfunction parseParamsFromQuery(queryValue: unknown): Record<string, unknown> {\n if (!queryValue) {\n return {};\n }\n\n if (typeof queryValue === 'string') {\n try {\n return JSON.parse(queryValue);\n } catch {\n return {};\n }\n }\n\n if (Array.isArray(queryValue)) {\n const last = queryValue[queryValue.length - 1];\n if (typeof last === 'string') {\n try {\n return JSON.parse(last);\n } catch {\n return {};\n }\n }\n return {};\n }\n\n if (typeof queryValue === 'object') {\n return Object.entries(queryValue).reduce<Record<string, unknown>>((acc, [key, value]) => {\n acc[key] = Array.isArray(value) ? value[value.length - 1] : value;\n return acc;\n }, {});\n }\n\n return {};\n}\n\nfunction coerceQueryValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value[value.length - 1];\n }\n return value ?? undefined;\n}\n\n/**\n * Create Invect Express Router\n */\nexport function createInvectRouter(config: InvectConfig): Router {\n const core = new Invect(config);\n\n // Initialize core asynchronously - this is a fire-and-forget initialization\n // The core will handle initialization state internally\n core\n .initialize()\n .then(async () => {\n // Start batch polling for automatic batch completion handling\n await core.startBatchPolling();\n // eslint-disable-next-line no-console\n console.log('✅ Invect batch polling started');\n\n // Start cron scheduler for automatic cron trigger execution\n await core.startCronScheduler();\n // eslint-disable-next-line no-console\n console.log('✅ Invect cron scheduler started');\n })\n .catch((error: unknown) => {\n // eslint-disable-next-line no-console\n console.error('Failed to initialize Invect Core:', error);\n });\n\n const router = Router();\n\n // Middleware to check if core is initialized\n router.use((req: Request, res: Response, next: NextFunction) => {\n if (!core.isInitialized()) {\n return res.status(503).json({\n error: 'Service Unavailable',\n message: 'Invect Core is still initializing. Please try again in a moment.',\n });\n }\n next();\n });\n\n // =====================================\n // AUTHENTICATION MIDDLEWARE\n // =====================================\n\n /**\n * Auth middleware - resolves identity from host app and attaches to request.\n *\n * The host app provides a `resolveUser` callback in the config that extracts\n * the user identity from the request (e.g., from JWT, session, API key).\n */\n router.use(async (req: Request, res: Response, next: NextFunction) => {\n // Always run plugin onRequest hooks so that identity is resolved even\n // when auth enforcement is disabled. Plugins such as @invect/user-auth\n // populate the identity from session cookies in this hook.\n try {\n const webRequestUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}`;\n const webRequestInit: RequestInit = {\n method: req.method,\n headers: req.headers as HeadersInit,\n };\n const webRequest = new globalThis.Request(webRequestUrl, webRequestInit);\n const hookContext = {\n path: req.path,\n method: req.method,\n identity: null as InvectIdentity | null,\n };\n\n const hookResult = await core.getPluginHookRunner().runOnRequest(webRequest, hookContext);\n if (hookResult.intercepted && hookResult.response) {\n const arrayBuf = await hookResult.response.arrayBuffer();\n res.status(hookResult.response.status);\n hookResult.response.headers.forEach((value, key) => {\n res.setHeader(key, value);\n });\n return res.send(Buffer.from(arrayBuf));\n }\n\n req.invectIdentity = hookContext.identity ?? null;\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('Auth resolution error:', error);\n req.invectIdentity = null;\n }\n\n next();\n });\n\n // =====================================\n // AUTHORIZATION HELPER\n // =====================================\n\n /**\n * Create an authorization middleware for a specific permission.\n * When useFlowAccessTable is enabled, looks up flow access from database.\n */\n function requirePermission(\n permission: InvectPermission,\n getResourceId?: (req: Request) => string | undefined,\n ) {\n return asyncHandler(async (req: Request, res: Response, next: NextFunction) => {\n const identity = req.invectIdentity ?? null;\n const resourceId = getResourceId ? getResourceId(req) : undefined;\n const resourceType = permission.split(':')[0] as InvectResourceType;\n\n // If useFlowAccessTable is enabled and this is a flow-related resource,\n // check access via the database instead of resourceAccess\n if (\n core.isFlowAccessTableEnabled() &&\n identity &&\n resourceId &&\n ['flow', 'flow-version', 'flow-run', 'node-execution'].includes(resourceType)\n ) {\n if (core.hasPermission(identity, 'admin:*')) {\n return next();\n }\n\n // For flow-related resources, check access via database\n const hasAccess = await core.hasFlowAccess(\n resourceId,\n identity.id,\n identity.teamIds || [],\n getRequiredFlowPermission(permission),\n );\n\n if (!hasAccess) {\n return res.status(403).json({\n error: 'Forbidden',\n message: `No access to flow '${resourceId}'`,\n });\n }\n\n return next();\n } else {\n // Standard authorization (uses resourceAccess from identity if provided)\n const result = await core.authorize({\n identity,\n action: permission,\n resource: resourceId\n ? {\n type: resourceType,\n id: resourceId,\n }\n : undefined,\n });\n\n if (!result.allowed) {\n const status = identity ? 403 : 401;\n return res.status(status).json({\n error: status === 403 ? 'Forbidden' : 'Unauthorized',\n message: result.reason || 'Access denied',\n });\n }\n }\n\n next();\n });\n }\n\n /**\n * Map permission to required flow access level.\n */\n function getRequiredFlowPermission(\n permission: InvectPermission,\n ): 'owner' | 'editor' | 'operator' | 'viewer' {\n // Destructive actions require owner\n if (permission.includes(':delete')) {\n return 'owner';\n }\n // Execution actions require operator\n if (permission.startsWith('flow-run:') || permission === 'node:test') {\n return 'operator';\n }\n // Modification actions require editor\n if (\n permission.includes(':create') ||\n permission.includes(':update') ||\n permission.includes(':publish')\n ) {\n return 'editor';\n }\n // Everything else requires viewer\n return 'viewer';\n }\n\n // =====================================\n // DASHBOARD ROUTES\n // =====================================\n\n /**\n * GET /dashboard/stats - Get dashboard statistics\n * Core method: ✅ getDashboardStats()\n * Returns: DashboardStats (flow counts, run counts by status, recent activity)\n */\n router.get(\n '/dashboard/stats',\n asyncHandler(async (_req: Request, res: Response) => {\n const stats = await core.getDashboardStats();\n res.json(stats);\n }),\n );\n\n // =====================================\n // FLOW MANAGEMENT ROUTES\n // =====================================\n\n /**\n * GET /flows/list - List all flows (simple GET endpoint)\n * Core method: ✅ listFlows()\n * Permission: flow:read\n */\n router.get(\n '/flows/list',\n requirePermission('flow:read'),\n asyncHandler(async (_req: Request, res: Response) => {\n const flows = await core.listFlows();\n res.json(flows);\n }),\n );\n\n /**\n * POST /flows/list - List flows with optional filtering and pagination\n * Core method: ✅ listFlows(options?: QueryOptions<Flow>)\n * Body: QueryOptions<Flow>\n * Permission: flow:read\n */\n router.post(\n '/flows/list',\n requirePermission('flow:read'),\n asyncHandler(async (req: Request, res: Response) => {\n const flows = await core.listFlows(req.body);\n res.json(flows);\n }),\n );\n\n /**\n * POST /flows - Create a new flow\n * Core method: ✅ createFlow(flowData: CreateFlowRequest)\n * Permission: flow:create\n */\n router.post(\n '/flows',\n requirePermission('flow:create'),\n asyncHandler(async (req: Request, res: Response) => {\n const flow = await core.createFlow(req.body);\n res.status(201).json(flow);\n }),\n );\n\n /**\n * GET /flows/:id - Get flow by ID\n * Core method: ✅ getFlow(flowId: string)\n * Permission: flow:read (with resource check)\n */\n router.get(\n '/flows/:id',\n requirePermission('flow:read', (req) => req.params.id),\n asyncHandler(async (req: Request, res: Response) => {\n const flow = await core.getFlow(req.params.id);\n res.json(flow);\n }),\n );\n\n /**\n * PUT /flows/:id - Update flow\n * Core method: ✅ updateFlow(flowId: string, updateData: UpdateFlowInput)\n * Permission: flow:update (with resource check)\n */\n router.put(\n '/flows/:id',\n requirePermission('flow:update', (req) => req.params.id),\n asyncHandler(async (req: Request, res: Response) => {\n const flow = await core.updateFlow(req.params.id, req.body);\n res.json(flow);\n }),\n );\n\n /**\n * DELETE /flows/:id - Delete flow\n * Core method: ✅ deleteFlow(flowId: string)\n * Permission: flow:delete (with resource check)\n */\n router.delete(\n '/flows/:id',\n requirePermission('flow:delete', (req) => req.params.id),\n asyncHandler(async (req: Request, res: Response) => {\n await core.deleteFlow(req.params.id);\n res.status(204).send();\n }),\n );\n\n /**\n * POST /validate-flow - Validate flow definition\n * Core method: ✅ validateFlowDefinition(flowId: string, flowDefinition: InvectDefinition)\n * Permission: flow:read\n */\n router.post(\n '/validate-flow',\n requirePermission('flow:read'),\n asyncHandler(async (req: Request, res: Response) => {\n const { flowId, flowDefinition } = req.body;\n const result = await core.validateFlowDefinition(flowId, flowDefinition);\n res.json(result);\n }),\n );\n\n /**\n * GET /flows/:flowId/react-flow - Get flow data in React Flow format\n * Core method: ✅ renderToReactFlow(flowId: string, options)\n * Query params: version, flowRunId\n */\n router.get(\n '/flows/:flowId/react-flow',\n asyncHandler(async (req: Request, res: Response) => {\n interface ReactFlowQueryParams {\n version?: string | 'latest';\n flowRunId?: string;\n }\n\n const queryParams = req.query as ReactFlowQueryParams;\n const options: Parameters<typeof core.renderToReactFlow>[1] = {};\n\n // Extract and validate query parameters\n if (queryParams.version) {\n options.version = queryParams.version;\n }\n if (queryParams.flowRunId) {\n options.flowRunId = queryParams.flowRunId;\n }\n\n const result = await core.renderToReactFlow(req.params.flowId, options);\n res.json(result);\n }),\n );\n\n // =====================================\n // FLOW VERSION MANAGEMENT ROUTES\n // =====================================\n\n /**\n * POST /flows/:id/versions/list - Get flow versions with optional filtering and pagination\n * Core method: ✅ listFlowVersions(flowId: string, options?: QueryOptions<FlowVersion>)\n * Body: QueryOptions<FlowVersion>\n */\n router.post(\n '/flows/:id/versions/list',\n asyncHandler(async (req: Request, res: Response) => {\n const versions = await core.listFlowVersions(req.params.id, req.body);\n res.json(versions);\n }),\n );\n\n /**\n * POST /flows/:id/versions - Create flow version\n * Core method: ✅ createFlowVersion(flowId: string, versionData: CreateFlowVersionRequest)\n * Permission: flow-version:create (with resource check on flow)\n */\n router.post(\n '/flows/:id/versions',\n requirePermission('flow-version:create', (req) => req.params.id),\n asyncHandler(async (req: Request, res: Response) => {\n const version = await core.createFlowVersion(req.params.id, req.body);\n res.status(201).json(version);\n }),\n );\n\n /**\n * GET /flows/:id/versions/:version - Get specific flow version (supports 'latest')\n * Core method: ✅ getFlowVersion(flowId: string, version: string | number | \"latest\")\n * Permission: flow-version:read (with resource check on flow)\n */\n router.get(\n '/flows/:id/versions/:version',\n requirePermission('flow-version:read', (req) => req.params.id),\n asyncHandler(async (req: Request, res: Response) => {\n const version = await core.getFlowVersion(req.params.id, req.params.version);\n if (!version) {\n return res.status(404).json({\n error: 'Not Found',\n message: `Version ${req.params.version} not found for flow ${req.params.id}`,\n });\n }\n res.json(version);\n }),\n );\n\n // =====================================\n // FLOW RUN EXECUTION ROUTES\n // =====================================\n\n /**\n * POST /flows/:flowId/run - Start flow execution (async - returns immediately)\n * Core method: ✅ startFlowRunAsync(flowId: string, inputs: FlowInputs, options?: ExecuteFlowOptions)\n * Returns immediately with flow run ID. The flow executes in the background.\n * Permission: flow-run:create (with resource check on flow)\n */\n router.post(\n '/flows/:flowId/run',\n requirePermission('flow-run:create', (req) => req.params.flowId),\n asyncHandler(async (req: Request, res: Response) => {\n const { inputs = {}, options } = req.body;\n const result = await core.startFlowRunAsync(req.params.flowId, inputs, options);\n res.status(201).json(result);\n }),\n );\n\n /**\n * POST /flows/:flowId/run-to-node/:nodeId - Execute flow up to a specific node\n * Only executes the upstream nodes required to produce output for the target node.\n * Core method: ✅ executeFlowToNode(flowId, targetNodeId, inputs, options)\n * Permission: flow-run:create (with resource check on flow)\n */\n router.post(\n '/flows/:flowId/run-to-node/:nodeId',\n requirePermission('flow-run:create', (req) => req.params.flowId),\n asyncHandler(async (req: Request, res: Response) => {\n const { inputs = {}, options } = req.body;\n const result = await core.executeFlowToNode(\n req.params.flowId,\n req.params.nodeId,\n inputs,\n options,\n );\n res.status(201).json(result);\n }),\n );\n\n /**\n * POST /flow-runs/list - Get all flow runs with optional filtering and pagination\n * Core method: ✅ listFlowRuns(options?: QueryOptions<FlowRun>)\n * Body: QueryOptions<FlowRun>\n * Permission: flow-run:read\n */\n router.post(\n '/flow-runs/list',\n requirePermission('flow-run:read'),\n asyncHandler(async (req: Request, res: Response) => {\n const flowRuns = await core.listFlowRuns(req.body);\n res.json(flowRuns);\n }),\n );\n\n /**\n * GET /flow-runs/:flowRunId - Get specific flow run by ID\n * Core method: ✅ getFlowRunById(flowRunId: string)\n */\n router.get(\n '/flow-runs/:flowRunId',\n asyncHandler(async (req: Request, res: Response) => {\n const flowRun = await core.getFlowRunById(req.params.flowRunId);\n res.json(flowRun);\n }),\n );\n\n /**\n * GET /flows/:flowId/flow-runs - Get flow runs for a specific flow\n * Core method: ✅ listFlowRunsByFlowId(flowId: string)\n */\n router.get(\n '/flows/:flowId/flow-runs',\n asyncHandler(async (req: Request, res: Response) => {\n const flowRuns = await core.listFlowRunsByFlowId(req.params.flowId);\n res.json(flowRuns);\n }),\n );\n\n /**\n * POST /flow-runs/:flowRunId/resume - Resume paused flow execution\n * Core method: ✅ resumeExecution(executionId: string)\n */\n router.post(\n '/flow-runs/:flowRunId/resume',\n asyncHandler(async (req: Request, res: Response) => {\n const result = await core.resumeExecution(req.params.flowRunId);\n res.json(result);\n }),\n );\n\n /**\n * POST /flow-runs/:flowRunId/cancel - Cancel flow execution\n * Core method: ✅ cancelFlowRun(flowRunId: string)\n */\n router.post(\n '/flow-runs/:flowRunId/cancel',\n asyncHandler(async (req: Request, res: Response) => {\n const result = await core.cancelFlowRun(req.params.flowRunId);\n res.json(result);\n }),\n );\n\n /**\n * POST /flow-runs/:flowRunId/pause - Pause flow execution\n * Core method: ✅ pauseFlowRun(flowRunId: string, reason?: string)\n */\n router.post(\n '/flow-runs/:flowRunId/pause',\n asyncHandler(async (req: Request, res: Response) => {\n const { reason } = req.body;\n const result = await core.pauseFlowRun(req.params.flowRunId, reason);\n res.json(result);\n }),\n );\n\n // =====================================\n // NODE EXECUTION ROUTES\n // =====================================\n\n /**\n * GET /flow-runs/:flowRunId/node-executions - Get node executions for a flow run\n * Core method: ✅ getNodeExecutionsByRunId(flowRunId: string)\n */\n router.get(\n '/flow-runs/:flowRunId/node-executions',\n asyncHandler(async (req: Request, res: Response) => {\n const nodeExecutions = await core.getNodeExecutionsByRunId(req.params.flowRunId);\n res.json(nodeExecutions);\n }),\n );\n\n /**\n * GET /flow-runs/:flowRunId/stream - SSE stream of execution events\n * Core method: ✅ createFlowRunEventStream(flowRunId: string)\n *\n * Streams node-execution and flow-run updates in real time.\n * First event is a \"snapshot\", then incremental updates, ending with \"end\".\n */\n router.get(\n '/flow-runs/:flowRunId/stream',\n asyncHandler(async (req: Request, res: Response) => {\n // SSE headers\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no');\n res.flushHeaders();\n\n try {\n const stream = core.createFlowRunEventStream(req.params.flowRunId);\n\n for await (const event of stream) {\n if (res.destroyed) {\n break;\n }\n const data = JSON.stringify(event);\n res.write(`event: ${event.type}\\ndata: ${data}\\n\\n`);\n }\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'Stream failed';\n if (res.headersSent) {\n res.write(`event: error\\ndata: ${JSON.stringify({ type: 'error', message })}\\n\\n`);\n } else {\n return res.status(500).json({ error: 'Internal Server Error', message });\n }\n } finally {\n res.end();\n }\n }),\n );\n\n /**\n * POST /node-executions/list - Get all node executions with optional filtering and pagination\n * Core method: ✅ listNodeExecutions(options?: QueryOptions<NodeExecution>)\n * Body: QueryOptions<NodeExecution>\n */\n router.post(\n '/node-executions/list',\n asyncHandler(async (req: Request, res: Response) => {\n const nodeExecutions = await core.listNodeExecutions(req.body);\n res.json(nodeExecutions);\n }),\n );\n\n // =====================================\n // NODE DATA & TESTING ROUTES\n // =====================================\n\n /**\n * POST /node-data/sql-query - Execute SQL query for testing\n * Core method: ✅ executeSqlQuery(request: SubmitSQLQueryRequest)\n */\n router.post(\n '/node-data/sql-query',\n asyncHandler(async (req: Request, res: Response) => {\n const result = await core.executeSqlQuery(req.body);\n res.json(result);\n }),\n );\n\n /**\n * POST /node-data/test-expression - Test a JS expression in the QuickJS sandbox\n * Core method: ✅ testJsExpression({ expression, context })\n */\n router.post(\n '/node-data/test-expression',\n asyncHandler(async (req: Request, res: Response) => {\n const result = await core.testJsExpression(req.body);\n res.json(result);\n }),\n );\n\n /**\n * POST /node-data/test-mapper - Test a mapper expression with mode semantics\n * Core method: ✅ testMapper({ expression, incomingData, mode? })\n */\n router.post(\n '/node-data/test-mapper',\n asyncHandler(async (req: Request, res: Response) => {\n const result = await core.testMapper(req.body);\n res.json(result);\n }),\n );\n\n /**\n * POST /node-data/model-query - Test model prompt\n * Core method: ✅ testModelPrompt(request: SubmitPromptRequest)\n */\n router.post(\n '/node-data/model-query',\n asyncHandler(async (req: Request, res: Response) => {\n const result = await core.testModelPrompt(req.body);\n res.json(result);\n }),\n );\n\n /**\n * GET /node-data/models - Get available AI models\n * Core method: ✅ getAvailableModels()\n */\n router.get(\n '/node-data/models',\n asyncHandler(async (req: Request, res: Response) => {\n const credentialId =\n typeof req.query.credentialId === 'string' ? req.query.credentialId.trim() : '';\n const providerQuery =\n typeof req.query.provider === 'string' ? req.query.provider.trim().toUpperCase() : '';\n\n if (credentialId) {\n const response = await core.getModelsForCredential(credentialId);\n res.json(response);\n return;\n }\n\n if (providerQuery) {\n if (!Object.values(BatchProvider).includes(providerQuery as BatchProvider)) {\n res.status(400).json({\n error: 'INVALID_PROVIDER',\n message: `Unsupported provider '${providerQuery}'. Expected one of: ${Object.values(BatchProvider).join(', ')}`,\n });\n return;\n }\n\n const provider = providerQuery as BatchProvider;\n const response = await core.getModelsForProvider(provider);\n res.json(response);\n return;\n }\n\n const models = await core.getAvailableModels();\n res.json(models);\n }),\n );\n\n /**\n * GET /node-data/databases - Get available databases\n * Core method: ✅ getAvailableDatabases()\n */\n router.get(\n '/node-data/databases',\n asyncHandler(async (req: Request, res: Response) => {\n const databases = core.getAvailableDatabases();\n res.json(databases);\n }),\n );\n\n /**\n * POST /node-config/update - Generic node configuration updates\n * Core method: ✅ handleNodeConfigUpdate(event: NodeConfigUpdateEvent)\n */\n router.post(\n '/node-config/update',\n asyncHandler(async (req: Request, res: Response) => {\n const response = await core.handleNodeConfigUpdate(req.body);\n res.json(response);\n }),\n );\n\n router.get(\n '/node-definition/:nodeType',\n asyncHandler(async (req: Request, res: Response) => {\n const rawNodeType = req.params.nodeType ?? '';\n\n // Accept both legacy GraphNodeType enum values (uppercase) and\n // action IDs (e.g. \"core.model\", \"gmail.send_message\").\n const nodeTypeParam = rawNodeType.includes('.')\n ? rawNodeType // action ID — pass through as-is\n : rawNodeType.toUpperCase(); // legacy — uppercase to match enum\n\n const isLegacyEnum = !rawNodeType.includes('.') && nodeTypeParam in GraphNodeType;\n const isActionId = rawNodeType.includes('.');\n\n if (!isLegacyEnum && !isActionId) {\n res.status(400).json({\n error: 'INVALID_NODE_TYPE',\n message: `Unknown node type '${req.params.nodeType}'`,\n });\n return;\n }\n\n const params = parseParamsFromQuery(req.query.params);\n const changeField =\n typeof req.query.changeField === 'string' ? req.query.changeField : undefined;\n const changeValue = coerceQueryValue(req.query.changeValue);\n const nodeId = typeof req.query.nodeId === 'string' ? req.query.nodeId : undefined;\n const flowId = typeof req.query.flowId === 'string' ? req.query.flowId : undefined;\n\n const response = await core.handleNodeConfigUpdate({\n nodeType: nodeTypeParam as GraphNodeType,\n nodeId: nodeId ?? `definition-${nodeTypeParam.toLowerCase()}`,\n flowId,\n params,\n change: changeField ? { field: changeField, value: changeValue } : undefined,\n });\n\n res.json(response);\n }),\n );\n\n /**\n * GET /nodes - Get available node definitions\n * Core method: ✅ getAvailableNodes()\n */\n router.get(\n '/nodes',\n asyncHandler(async (req: Request, res: Response) => {\n const nodes = core.getAvailableNodes();\n res.json(nodes);\n }),\n );\n\n /**\n * GET /actions/:actionId/fields/:fieldName/options - Load dynamic field options\n * Core method: ✅ resolveFieldOptions(actionId, fieldName, deps)\n *\n * Query params:\n * deps - JSON-encoded object of dependency field values\n */\n router.get(\n '/actions/:actionId/fields/:fieldName/options',\n asyncHandler(async (req: Request, res: Response) => {\n const { actionId, fieldName } = req.params;\n\n let dependencyValues: Record<string, unknown> = {};\n if (typeof req.query.deps === 'string') {\n try {\n dependencyValues = JSON.parse(req.query.deps);\n } catch {\n res.status(400).json({ error: 'Invalid deps JSON' });\n return;\n }\n }\n\n const result = await core.resolveFieldOptions(actionId, fieldName, dependencyValues);\n res.json(result);\n }),\n );\n\n /**\n * POST /nodes/test - Test/execute a single node in isolation\n * Core method: ✅ testNode(nodeType, params, inputData)\n * Body: { nodeType: string, params: Record<string, unknown>, inputData?: Record<string, unknown> }\n */\n router.post(\n '/nodes/test',\n asyncHandler(async (req: Request, res: Response) => {\n const { nodeType, params, inputData } = req.body;\n\n if (!nodeType || typeof nodeType !== 'string') {\n return res.status(400).json({ error: 'nodeType is required and must be a string' });\n }\n\n if (!params || typeof params !== 'object') {\n return res.status(400).json({ error: 'params is required and must be an object' });\n }\n\n const result = await core.testNode(nodeType, params, inputData || {});\n res.json(result);\n }),\n );\n\n // =====================================\n // CREDENTIALS MANAGEMENT ROUTES\n // =====================================\n\n /**\n * POST /credentials - Create a new credential\n * Core method: ✅ createCredential(input: CreateCredentialInput)\n * Permission: credential:create\n */\n router.post(\n '/credentials',\n requirePermission('credential:create'),\n asyncHandler(async (req: Request, res: Response) => {\n // Resolve userId from auth context, body, or header; fallback to identity id or 'anonymous'\n const resolvedUserId =\n req.invectIdentity?.id ||\n (req as Request & { user?: { id?: string } }).user?.id ||\n req.body.userId ||\n req.header('x-user-id') ||\n 'anonymous';\n\n const credential = await core.createCredential({ ...req.body, userId: resolvedUserId });\n res.status(201).json(credential);\n }),\n );\n\n /**\n * GET /credentials - List credentials with optional filtering and pagination\n * Core method: ✅ listCredentials(filters?: CredentialFilters, options?: QueryOptions)\n * Query params: type?, authType?, isActive?, page?, limit?\n * Permission: credential:read\n */\n router.get(\n '/credentials',\n requirePermission('credential:read'),\n asyncHandler(async (req: Request, res: Response) => {\n const filters: CredentialFilters = {\n type: req.query.type as CredentialFilters['type'],\n authType: req.query.authType as CredentialFilters['authType'],\n isActive:\n req.query.isActive === 'true' ? true : req.query.isActive === 'false' ? false : undefined,\n };\n const credentials = await core.listCredentials(filters);\n res.json(credentials);\n }),\n );\n\n /**\n * GET /credentials/:id - Get credential by ID\n * Core method: ✅ getCredential(id: string)\n * Permission: credential:read (with resource check)\n */\n router.get(\n '/credentials/:id',\n requirePermission('credential:read', (req) => req.params.id),\n asyncHandler(async (req: Request, res: Response) => {\n const credential = await core.getCredential(req.params.id);\n res.json(credential);\n }),\n );\n\n /**\n * PUT /credentials/:id - Update credential\n * Core method: ✅ updateCredential(id: string, input: UpdateCredentialInput)\n * Permission: credential:update (with resource check)\n */\n router.put(\n '/credentials/:id',\n requirePermission('credential:update', (req) => req.params.id),\n asyncHandler(async (req: Request, res: Response) => {\n const credential = await core.updateCredential(req.params.id, req.body);\n res.json(credential);\n }),\n );\n\n /**\n * DELETE /credentials/:id - Delete credential\n * Core method: ✅ deleteCredential(id: string)\n * Permission: credential:delete (with resource check)\n */\n router.delete(\n '/credentials/:id',\n requirePermission('credential:delete', (req) => req.params.id),\n asyncHandler(async (req: Request, res: Response) => {\n await core.deleteCredential(req.params.id);\n res.status(204).send();\n }),\n );\n\n /**\n * POST /credentials/:id/test - Test credential validity\n * Core method: ✅ testCredential(id: string)\n * Permission: credential:read (with resource check)\n */\n router.post(\n '/credentials/:id/test',\n requirePermission('credential:read', (req) => req.params.id),\n asyncHandler(async (req: Request, res: Response) => {\n const result = await core.testCredential(req.params.id);\n res.json(result);\n }),\n );\n\n /**\n * POST /credentials/:id/track-usage - Update credential last used timestamp\n * Core method: ✅ updateCredentialLastUsed(id: string)\n * Permission: credential:read (with resource check)\n */\n router.post(\n '/credentials/:id/track-usage',\n requirePermission('credential:read', (req) => req.params.id),\n asyncHandler(async (req: Request, res: Response) => {\n await core.updateCredentialLastUsed(req.params.id);\n res.status(204).send();\n }),\n );\n\n /**\n * GET /credentials/:id/webhook - Get webhook info for a credential\n * Core method: ✅ CredentialsService.getWebhookInfo(id)\n * Permission: credential:read (with resource check)\n */\n router.get(\n '/credentials/:id/webhook',\n requirePermission('credential:read', (req) => req.params.id),\n asyncHandler(async (req: Request, res: Response) => {\n const webhookInfo = await core.getCredentialsService().getWebhookInfo(req.params.id);\n if (!webhookInfo) {\n return res.status(404).json({ error: 'Webhook not enabled for credential' });\n }\n res.json(webhookInfo);\n }),\n );\n\n /**\n * GET /credentials/:id/webhook-info - Backwards-compatible alias for webhook info\n */\n router.get(\n '/credentials/:id/webhook-info',\n requirePermission('credential:read', (req) => req.params.id),\n asyncHandler(async (req: Request, res: Response) => {\n const webhookInfo = await core.getCredentialsService().getWebhookInfo(req.params.id);\n if (!webhookInfo) {\n return res.status(404).json({ error: 'Webhook not enabled for credential' });\n }\n res.json(webhookInfo);\n }),\n );\n\n /**\n * POST /credentials/:id/webhook - Enable webhook for a credential\n * Core method: ✅ CredentialsService.enableWebhook(id)\n * Permission: credential:update (with resource check)\n */\n router.post(\n '/credentials/:id/webhook',\n requirePermission('credential:update', (req) => req.params.id),\n asyncHandler(async (req: Request, res: Response) => {\n const webhookInfo = await core.getCredentialsService().enableWebhook(req.params.id);\n res.json(webhookInfo);\n }),\n );\n\n /**\n * POST /credentials/:id/webhook/enable - Backwards-compatible alias for enabling webhooks\n */\n router.post(\n '/credentials/:id/webhook/enable',\n requirePermission('credential:update', (req) => req.params.id),\n asyncHandler(async (req: Request, res: Response) => {\n const webhookInfo = await core.getCredentialsService().enableWebhook(req.params.id);\n res.json(webhookInfo);\n }),\n );\n\n /**\n * POST /webhooks/credentials/:webhookPath - Public credential webhook ingestion endpoint\n * Core methods: ✅ CredentialsService.findByWebhookPath(webhookPath), updateCredentialLastUsed(id)\n */\n router.post(\n '/webhooks/credentials/:webhookPath',\n asyncHandler(async (req: Request, res: Response) => {\n const credential = await core\n .getCredentialsService()\n .findByWebhookPath(req.params.webhookPath);\n\n if (!credential) {\n return res.status(404).json({ ok: false, error: 'Credential webhook not found' });\n }\n\n res.json({\n ok: true,\n credentialId: credential.id,\n triggeredFlows: 0,\n runs: [],\n body: req.body ?? null,\n });\n }),\n );\n\n /**\n * GET /credentials/expiring - Get credentials expiring soon\n * Core method: ✅ getExpiringCredentials(daysUntilExpiry?: number)\n * Query params: daysUntilExpiry (default: 7)\n * Permission: credential:read\n */\n router.get(\n '/credentials/expiring',\n requirePermission('credential:read'),\n asyncHandler(async (req: Request, res: Response) => {\n const daysUntilExpiry = req.query.daysUntilExpiry\n ? parseInt(req.query.daysUntilExpiry as string)\n : 7;\n\n const credentials = await core.getExpiringCredentials(daysUntilExpiry);\n res.json(credentials);\n }),\n );\n\n /**\n * POST /credentials/test-request - Test a credential by making an HTTP request\n * This endpoint proxies HTTP requests to avoid CORS issues when testing credentials\n * Body: { url, method, headers, body }\n */\n router.post(\n '/credentials/test-request',\n asyncHandler(async (req: Request, res: Response) => {\n const { url, method = 'GET', headers = {}, body } = req.body;\n\n if (!url) {\n res.status(400).json({ error: 'URL is required' });\n return;\n }\n\n try {\n const fetchOptions: RequestInit = {\n method,\n headers: headers as Record<string, string>,\n };\n\n if (body && ['POST', 'PUT', 'PATCH'].includes(method)) {\n fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);\n }\n\n const response = await fetch(url, fetchOptions);\n const responseText = await response.text();\n\n // Try to parse as JSON, fallback to text\n let responseBody: unknown;\n try {\n responseBody = JSON.parse(responseText);\n } catch {\n responseBody = responseText;\n }\n\n res.json({\n status: response.status,\n statusText: response.statusText,\n ok: response.ok,\n body: responseBody,\n });\n } catch (error) {\n res.status(500).json({\n error: 'Request failed',\n message: error instanceof Error ? error.message : 'Unknown error',\n });\n }\n }),\n );\n\n // =====================================\n // OAUTH2 ROUTES\n // =====================================\n\n /**\n * GET /credentials/oauth2/providers - List all available OAuth2 providers\n */\n router.get(\n '/credentials/oauth2/providers',\n asyncHandler(async (_req: Request, res: Response) => {\n const providers = core.getOAuth2Providers();\n res.json(providers);\n }),\n );\n\n /**\n * GET /credentials/oauth2/providers/:providerId - Get a specific OAuth2 provider\n */\n router.get(\n '/credentials/oauth2/providers/:providerId',\n asyncHandler(async (req: Request, res: Response) => {\n const provider = core.getOAuth2Provider(req.params.providerId);\n if (!provider) {\n res.status(404).json({ error: 'OAuth2 provider not found' });\n return;\n }\n res.json(provider);\n }),\n );\n\n /**\n * POST /credentials/oauth2/start - Start OAuth2 authorization flow\n * Body: { providerId, clientId, clientSecret, redirectUri, scopes?, returnUrl?, credentialName? }\n * Returns: { authorizationUrl, state }\n */\n router.post(\n '/credentials/oauth2/start',\n asyncHandler(async (req: Request, res: Response) => {\n const { providerId, clientId, clientSecret, redirectUri, scopes, returnUrl, credentialName } =\n req.body;\n\n if (!providerId || !clientId || !clientSecret || !redirectUri) {\n res.status(400).json({\n error: 'Missing required fields: providerId, clientId, clientSecret, redirectUri',\n });\n return;\n }\n\n const result = core.startOAuth2Flow(\n providerId,\n { clientId, clientSecret, redirectUri },\n { scopes, returnUrl, credentialName },\n );\n\n res.json(result);\n }),\n );\n\n /**\n * POST /credentials/oauth2/callback - Handle OAuth2 callback and exchange code for tokens\n * Body: { code, state, clientId, clientSecret, redirectUri }\n * Creates a new credential with the obtained tokens\n */\n router.post(\n '/credentials/oauth2/callback',\n asyncHandler(async (req: Request, res: Response) => {\n const { code, state, clientId, clientSecret, redirectUri } = req.body;\n\n if (!code || !state || !clientId || !clientSecret || !redirectUri) {\n res.status(400).json({\n error: 'Missing required fields: code, state, clientId, clientSecret, redirectUri',\n });\n return;\n }\n\n const credential = await core.handleOAuth2Callback(code, state, {\n clientId,\n clientSecret,\n redirectUri,\n });\n\n res.json(credential);\n }),\n );\n\n /**\n * GET /credentials/oauth2/callback - Handle OAuth2 callback (for redirect-based flows)\n * Query: code, state\n * Redirects to the return URL with credential ID or error\n */\n router.get(\n '/credentials/oauth2/callback',\n asyncHandler(async (req: Request, res: Response) => {\n const { code, state, error, error_description } = req.query;\n\n // Check for OAuth error\n if (error) {\n const errorMsg = error_description || error;\n // Get pending state to find return URL\n const pendingState = core.getOAuth2PendingState(state as string);\n const returnUrl = pendingState?.returnUrl || '/';\n const separator = returnUrl.includes('?') ? '&' : '?';\n res.redirect(\n `${returnUrl}${separator}oauth_error=${encodeURIComponent(errorMsg as string)}`,\n );\n return;\n }\n\n if (!code || !state) {\n res.status(400).json({ error: 'Missing code or state parameter' });\n return;\n }\n\n // Get pending state to retrieve client credentials and return URL\n const pendingState = core.getOAuth2PendingState(state as string);\n if (!pendingState) {\n res.status(400).json({ error: 'Invalid or expired OAuth state' });\n return;\n }\n\n // For GET callback, we need the client credentials to be stored in session or config\n // This is a simplified flow - in production, you'd store these securely\n res.json({\n message:\n 'OAuth callback received. Use POST /credentials/oauth2/callback to exchange the code.',\n code,\n state,\n providerId: pendingState.providerId,\n returnUrl: pendingState.returnUrl,\n });\n }),\n );\n\n /**\n * POST /credentials/:id/refresh - Manually refresh an OAuth2 credential's access token\n */\n router.post(\n '/credentials/:id/refresh',\n asyncHandler(async (req: Request, res: Response) => {\n const credential = await core.refreshOAuth2Credential(req.params.id);\n res.json(credential);\n }),\n );\n\n // =====================================\n // TRIGGER MANAGEMENT ROUTES\n // =====================================\n\n /**\n * GET /flows/:flowId/triggers - List all trigger registrations for a flow\n * Core method: ✅ listTriggersForFlow(flowId)\n * Permission: flow:read (with resource check)\n */\n router.get(\n '/flows/:flowId/triggers',\n requirePermission('flow:read', (req) => req.params.flowId),\n asyncHandler(async (req: Request, res: Response) => {\n const triggers = await core.listTriggersForFlow(req.params.flowId);\n res.json(triggers);\n }),\n );\n\n /**\n * POST /flows/:flowId/triggers - Create a trigger registration for a flow\n * Core method: ✅ createTrigger(input)\n * Permission: flow:update (with resource check)\n */\n router.post(\n '/flows/:flowId/triggers',\n requirePermission('flow:update', (req) => req.params.flowId),\n asyncHandler(async (req: Request, res: Response) => {\n const trigger = await core.createTrigger({\n ...req.body,\n flowId: req.params.flowId,\n });\n res.status(201).json(trigger);\n }),\n );\n\n /**\n * POST /flows/:flowId/triggers/sync - Sync triggers from the flow definition\n * Core method: ✅ syncTriggersForFlow(flowId, definition)\n * Permission: flow:update (with resource check)\n */\n router.post(\n '/flows/:flowId/triggers/sync',\n requirePermission('flow:update', (req) => req.params.flowId),\n asyncHandler(async (req: Request, res: Response) => {\n const { definition } = req.body;\n const triggers = await core.syncTriggersForFlow(req.params.flowId, definition);\n res.json(triggers);\n }),\n );\n\n /**\n * GET /triggers/:triggerId - Get a single trigger by ID\n * Core method: ✅ getTrigger(triggerId)\n */\n router.get(\n '/triggers/:triggerId',\n asyncHandler(async (req: Request, res: Response) => {\n const trigger = await core.getTrigger(req.params.triggerId);\n if (!trigger) {\n return res.status(404).json({\n error: 'Not Found',\n message: `Trigger ${req.params.triggerId} not found`,\n });\n }\n res.json(trigger);\n }),\n );\n\n /**\n * PUT /triggers/:triggerId - Update a trigger registration\n * Core method: ✅ updateTrigger(triggerId, input)\n */\n router.put(\n '/triggers/:triggerId',\n asyncHandler(async (req: Request, res: Response) => {\n const trigger = await core.updateTrigger(req.params.triggerId, req.body);\n if (!trigger) {\n return res.status(404).json({\n error: 'Not Found',\n message: `Trigger ${req.params.triggerId} not found`,\n });\n }\n res.json(trigger);\n }),\n );\n\n /**\n * DELETE /triggers/:triggerId - Delete a trigger registration\n * Core method: ✅ deleteTrigger(triggerId)\n */\n router.delete(\n '/triggers/:triggerId',\n asyncHandler(async (req: Request, res: Response) => {\n await core.deleteTrigger(req.params.triggerId);\n res.status(204).send();\n }),\n );\n\n // =====================================\n // AGENT TOOLS ROUTES\n // =====================================\n\n /**\n * GET /agent/tools - List all available agent tools\n * Core method: ✅ getAgentTools()\n */\n router.get(\n '/agent/tools',\n asyncHandler(async (_req: Request, res: Response) => {\n const tools = core.getAgentTools();\n res.json(tools);\n }),\n );\n\n // =====================================\n // CHAT ASSISTANT ROUTES\n // =====================================\n\n /**\n * GET /chat/status - Check if chat assistant is enabled\n * Core method: ✅ isChatEnabled()\n */\n router.get(\n '/chat/status',\n asyncHandler(async (_req: Request, res: Response) => {\n res.json({ enabled: core.isChatEnabled() });\n }),\n );\n\n /**\n * POST /chat - Streaming chat assistant endpoint (SSE)\n * Core method: ✅ createChatStream()\n *\n * Request body: { messages: ChatMessage[], context: ChatContext }\n * Response: Server-Sent Events stream of ChatStreamEvents\n */\n router.post(\n '/chat',\n asyncHandler(async (req: Request, res: Response) => {\n const { messages, context } = req.body;\n\n if (!messages || !Array.isArray(messages)) {\n return res.status(400).json({\n error: 'Validation Error',\n message: '\"messages\" must be an array of chat messages',\n });\n }\n\n // Set SSE headers\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no'); // Disable nginx buffering\n res.flushHeaders();\n\n // Resolve identity for RBAC (from middleware)\n const identity =\n (req as Request & { __invectIdentity?: InvectIdentity | null }).__invectIdentity ??\n undefined;\n\n try {\n const stream = await core.createChatStream({\n messages,\n context: context || {},\n identity,\n });\n\n // Stream events as SSE frames\n for await (const event of stream) {\n // Check if client disconnected\n if (res.destroyed) {\n break;\n }\n\n const data = JSON.stringify(event);\n res.write(`event: ${event.type}\\ndata: ${data}\\n\\n`);\n }\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'Chat stream failed';\n // If headers already sent, write error as SSE event\n if (res.headersSent) {\n res.write(\n `event: error\\ndata: ${JSON.stringify({ type: 'error', message, recoverable: false })}\\n\\n`,\n );\n } else {\n return res.status(500).json({ error: 'Internal Server Error', message });\n }\n } finally {\n res.end();\n }\n }),\n );\n\n // =====================================\n // CHAT MESSAGE PERSISTENCE ROUTES\n // =====================================\n\n /**\n * GET /chat/messages/:flowId - Get persisted chat messages for a flow\n * Core method: ✅ getChatMessages()\n */\n router.get(\n '/chat/messages/:flowId',\n asyncHandler(async (req: Request, res: Response) => {\n const messages = await core.getChatMessages(req.params.flowId);\n res.json(messages);\n }),\n );\n\n /**\n * PUT /chat/messages/:flowId - Save (replace) chat messages for a flow\n * Core method: ✅ saveChatMessages()\n *\n * Request body: { messages: Array<{ role, content, toolMeta? }> }\n */\n router.put(\n '/chat/messages/:flowId',\n asyncHandler(async (req: Request, res: Response) => {\n const { messages } = req.body;\n if (!messages || !Array.isArray(messages)) {\n return res.status(400).json({\n error: 'Validation Error',\n message: '\"messages\" must be an array',\n });\n }\n const saved = await core.saveChatMessages(req.params.flowId, messages);\n res.json(saved);\n }),\n );\n\n /**\n * DELETE /chat/messages/:flowId - Delete all chat messages for a flow\n * Core method: ✅ deleteChatMessages()\n */\n router.delete(\n '/chat/messages/:flowId',\n asyncHandler(async (req: Request, res: Response) => {\n await core.deleteChatMessages(req.params.flowId);\n res.json({ success: true });\n }),\n );\n\n // =====================================\n // PLUGIN ENDPOINTS\n // Mount API endpoints defined by plugins via core.getPluginEndpoints()\n // =====================================\n router.all(\n '/plugins/*',\n asyncHandler(async (req: Request, res: Response) => {\n const endpoints = core.getPluginEndpoints();\n // Strip the /plugins prefix — endpoint paths are defined relative to it\n // e.g. req.path=\"/plugins/auth/api/auth/sign-in/email\" → \"/auth/api/auth/sign-in/email\"\n const pluginPath = (req.path || '/').replace(/^\\/plugins/, '') || '/';\n const method = req.method.toUpperCase();\n\n const matchedEndpoint = endpoints.find((ep) => {\n if (ep.method !== method) {\n return false;\n }\n // Path matching with Express-style :params and * wildcards\n const pattern = ep.path\n .replace(/\\*/g, '(.*)') // wildcard * → match any path segments\n .replace(/:([^/]+)/g, '([^/]+)'); // :param → match single segment\n return new RegExp(`^${pattern}$`).test(pluginPath);\n });\n\n if (!matchedEndpoint) {\n return res.status(404).json({\n error: 'Not Found',\n message: `Plugin route ${method} ${pluginPath} not found`,\n });\n }\n\n // Extract path params\n const paramNames: string[] = [];\n const paramPattern = matchedEndpoint.path\n .replace(/\\*/g, '(.*)') // wildcard * → match rest of path\n .replace(/:([^/]+)/g, (_m, name) => {\n paramNames.push(name);\n return '([^/]+)';\n });\n const paramMatch = new RegExp(`^${paramPattern}$`).exec(pluginPath);\n const params: Record<string, string> = {};\n if (paramMatch) {\n paramNames.forEach((name, i) => {\n params[name] = paramMatch[i + 1] || '';\n });\n }\n\n // Check endpoint-level auth\n if (!matchedEndpoint.isPublic && matchedEndpoint.permission) {\n const identity = req.invectIdentity ?? null;\n if (!core.hasPermission(identity, matchedEndpoint.permission)) {\n return res.status(403).json({\n error: 'Forbidden',\n message: `Missing permission: ${matchedEndpoint.permission}`,\n });\n }\n }\n\n // Build a Web Request from the Express req so plugin handlers\n // (e.g. better-auth) that rely on the Fetch API Request work correctly.\n const webRequestUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}`;\n const webRequestInit: RequestInit = {\n method: req.method,\n headers: req.headers as HeadersInit,\n };\n if (req.method !== 'GET' && req.method !== 'HEAD' && req.method !== 'DELETE') {\n // Re-serialize the already-parsed body so the stream is consumable\n webRequestInit.body = JSON.stringify(req.body || {});\n }\n const webRequest = new globalThis.Request(webRequestUrl, webRequestInit);\n\n const result = await matchedEndpoint.handler({\n body: req.body || {},\n params,\n query: (req.query || {}) as Record<string, string | undefined>,\n headers: req.headers as Record<string, string | undefined>,\n identity: req.invectIdentity ?? null,\n database: createPluginDatabaseApi(core),\n request: webRequest,\n core: {\n getPermissions: (identity) => core.getPermissions(identity),\n getAvailableRoles: () => core.getAvailableRoles(),\n getResolvedRole: (identity) => core.getAuthService().getResolvedRole(identity),\n isFlowAccessTableEnabled: () => core.isFlowAccessTableEnabled(),\n listFlowAccess: (flowId) => core.listFlowAccess(flowId),\n grantFlowAccess: (input) => core.grantFlowAccess(input),\n revokeFlowAccess: (accessId) => core.revokeFlowAccess(accessId),\n getAccessibleFlowIds: (userId, teamIds) => core.getAccessibleFlowIds(userId, teamIds),\n getFlowPermission: (flowId, userId, teamIds) =>\n core.getFlowPermission(flowId, userId, teamIds),\n authorize: (context) => core.authorize(context),\n },\n });\n\n // Handle raw Response objects\n if (result instanceof Response) {\n const arrayBuf = await result.arrayBuffer();\n res.status(result.status);\n // Forward all headers — but handle Set-Cookie specially since\n // Headers.forEach() combines multiple Set-Cookie values into one\n // comma-separated string, which breaks cookie parsing.\n result.headers.forEach((value, key) => {\n if (key.toLowerCase() !== 'set-cookie') {\n res.setHeader(key, value);\n }\n });\n // Use getSetCookie() to get individual Set-Cookie headers intact\n const setCookies = result.headers.getSetCookie?.();\n if (setCookies && setCookies.length > 0) {\n res.setHeader('set-cookie', setCookies);\n }\n res.send(Buffer.from(arrayBuf));\n return;\n }\n\n // Handle streaming responses\n if ('stream' in result && result.stream) {\n res.status(result.status || 200);\n res.setHeader('Content-Type', 'text/event-stream');\n const reader = result.stream.getReader();\n const pump = async () => {\n const { done, value } = await reader.read();\n if (done) {\n res.end();\n return;\n }\n res.write(value);\n await pump();\n };\n await pump();\n return;\n }\n\n // Standard JSON response\n const jsonResult = result as { status?: number; body: unknown };\n res.status(jsonResult.status || 200).json(jsonResult.body);\n }),\n );\n\n // Error handling middleware - must be last\n router.use((error: Error, _req: Request, res: Response, _next: NextFunction) => {\n // Handle Zod validation errors\n if (error instanceof ZodError) {\n return res.status(400).json({\n error: 'Validation Error',\n message: 'Invalid request data',\n details: error.errors.map((err) => ({\n path: err.path.join('.'),\n message: err.message,\n code: err.code,\n })),\n });\n }\n\n // Handle other known errors\n if (error.name === 'DatabaseError') {\n return res.status(500).json({\n error: 'Database Error',\n message: error.message || 'A database error occurred',\n });\n }\n\n // Handle generic errors\n // eslint-disable-next-line no-console\n console.error('Invect Router Error:', error);\n res.status(500).json({\n error: 'Internal Server Error',\n message: 'An unexpected error occurred',\n });\n });\n\n return router;\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,86 @@
1
+ {
2
+ "name": "@invect/express",
3
+ "version": "0.0.1",
4
+ "description": "Express.js integration package for Invect Core",
5
+ "main": "dist/index.cjs",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "type": "module",
9
+ "files": [
10
+ "dist/**/*"
11
+ ],
12
+ "keywords": [
13
+ "invect",
14
+ "express",
15
+ "workflow",
16
+ "automation",
17
+ "api"
18
+ ],
19
+ "author": "robase",
20
+ "license": "MIT",
21
+ "dependencies": {
22
+ "express": "^4.19.2",
23
+ "cors": "^2.8.5",
24
+ "@invect/core": "0.0.1"
25
+ },
26
+ "devDependencies": {
27
+ "@eslint/js": "^9.31.0",
28
+ "@types/express": "^4.17.21",
29
+ "@types/cors": "^2.8.17",
30
+ "@types/node": "^20.11.20",
31
+ "eslint": "^9.31.0",
32
+ "globals": "^16.3.0",
33
+ "prettier": "^3.6.2",
34
+ "tsdown": "^0.21.1",
35
+ "typescript": "^5.3.3",
36
+ "typescript-eslint": "^8.36.0",
37
+ "vitest": "^1.2.2",
38
+ "supertest": "^6.3.4",
39
+ "@types/supertest": "^6.0.2"
40
+ },
41
+ "peerDependencies": {
42
+ "express": "^4.18.0"
43
+ },
44
+ "exports": {
45
+ ".": {
46
+ "types": "./dist/index.d.ts",
47
+ "import": "./dist/index.js",
48
+ "require": "./dist/index.cjs"
49
+ },
50
+ "./router": {
51
+ "types": "./dist/router/index.d.ts",
52
+ "import": "./dist/router/index.js",
53
+ "require": "./dist/router/index.cjs"
54
+ },
55
+ "./middleware": {
56
+ "types": "./dist/middleware/index.d.ts",
57
+ "import": "./dist/middleware/index.js",
58
+ "require": "./dist/middleware/index.cjs"
59
+ }
60
+ },
61
+ "repository": {
62
+ "type": "git",
63
+ "url": "https://github.com/robase/invect.git",
64
+ "directory": "pkg/express"
65
+ },
66
+ "bugs": {
67
+ "url": "https://github.com/robase/invect/issues"
68
+ },
69
+ "homepage": "https://invect.dev",
70
+ "publishConfig": {
71
+ "access": "public"
72
+ },
73
+ "scripts": {
74
+ "build": "tsdown",
75
+ "build:w": "tsdown --watch",
76
+ "dev": "tsdown --watch",
77
+ "clean": "rm -rf dist",
78
+ "format": "prettier --write \"src/**/*.ts\"",
79
+ "typecheck": "tsc --noEmit",
80
+ "lint": "eslint src",
81
+ "lint:fix": "eslint src --fix",
82
+ "test": "echo \"No tests configured\"",
83
+ "test:watch": "echo \"No tests configured\"",
84
+ "test:coverage": "echo \"No tests configured\""
85
+ }
86
+ }