Milestone 0: import zappier billing, Verae middleware, and Zapier research
Compose-ready workspace: packages/zappier (rate card, portal, Stripe), packages/verae-zapier-middleware (timestamp + NATS), packages/verae-zapier (CLI app), vendor/zapier-platform, and research/zapier vendor corpus. Gate 0 structure checks pass. Product code and research are not yet wired.
This commit is contained in:
commit
b4150c8250
1364 changed files with 6814366 additions and 0 deletions
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* @fileoverview Structured errors and Express helpers.
|
||||
* @module errors
|
||||
*/
|
||||
|
||||
import { createDebugger } from './debug/logger.js';
|
||||
import { getTraceId } from './debug/trace-context.js';
|
||||
|
||||
const log = createDebugger('app');
|
||||
|
||||
/**
|
||||
* Operational error with HTTP status and machine-readable code.
|
||||
*/
|
||||
export class AppError extends Error {
|
||||
/**
|
||||
* @param {string} message - Human-readable error.
|
||||
* @param {object} [options]
|
||||
* @param {number} [options.status=500] - HTTP status code.
|
||||
* @param {string} [options.code='INTERNAL_ERROR'] - Machine code.
|
||||
* @param {unknown} [options.details] - Optional structured details (safe for clients).
|
||||
*/
|
||||
constructor(message, { status = 500, code = 'INTERNAL_ERROR', details } = {}) {
|
||||
super(message);
|
||||
this.name = 'AppError';
|
||||
this.status = status;
|
||||
this.code = code;
|
||||
this.details = details;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap an async Express handler so rejected promises reach the error middleware.
|
||||
*
|
||||
* @param {(req: import('express').Request, res: import('express').Response, next: import('express').NextFunction) => Promise<unknown>} fn
|
||||
* @returns {import('express').RequestHandler}
|
||||
*/
|
||||
export function asyncHandler(fn) {
|
||||
return (req, res, next) => {
|
||||
Promise.resolve(fn(req, res, next)).catch(next);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a JSON error response. Logs server errors when debug is enabled.
|
||||
*
|
||||
* @param {import('express').Response} res
|
||||
* @param {Error|AppError} err
|
||||
* @returns {void}
|
||||
*/
|
||||
export function sendError(res, err) {
|
||||
const status = err instanceof AppError ? err.status : 500;
|
||||
const code = err instanceof AppError ? err.code : 'INTERNAL_ERROR';
|
||||
const message = err?.message || 'Internal server error';
|
||||
const details = err instanceof AppError ? err.details : undefined;
|
||||
const traceId = getTraceId();
|
||||
|
||||
if (status >= 500) {
|
||||
log.error('request failed', {
|
||||
status,
|
||||
code,
|
||||
message,
|
||||
traceId,
|
||||
stack: err?.stack,
|
||||
});
|
||||
} else {
|
||||
log.debug('client error', { status, code, message, traceId });
|
||||
}
|
||||
|
||||
res.status(status).json({
|
||||
error: message,
|
||||
code,
|
||||
...(details !== undefined ? { details } : {}),
|
||||
...(traceId ? { traceId } : {}),
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue