Features · Install · Usage · Platform
The foundation utilities that power every Smoo AI service. They handle the repetitive infrastructure work — error handling, request tracking, validation, environment detection — so you focus on features. Type-safe, production-tested, zero configuration.
Stop copy-pasting the same utility functions across projects. One package gives you:
- ✅ Production-tested utilities used across every Smoo AI service
- ✅ Type-safe implementations with full TypeScript support
- ✅ AWS Lambda integration that just works
- ✅ Human-readable error messages your support team will thank you for
- ✅ Sensible defaults out of the box — no configuration required
Concretely, that means a battle-tested Lambda apiHandler, schema validation that produces human-readable errors, case-insensitive collections for HTTP headers, production-ready Hono apps for Lambda, file discovery, environment detection, type-safe error handling, async helpers, and global phone-number validation.
pnpm add @smooai/utilsStop writing try-catch blocks in every Lambda function. The apiHandler wrapper handles parsing, validation, error handling, logging, and response formatting:
import { apiHandler } from '@smooai/utils';
// Before: boilerplate everywhere
export const handler = async (event, context) => {
try {
// Parse body
// Validate input
// Handle errors
// Format response
// Log everything
} catch (error) {
// More error handling
}
};
// After: focus on your logic
export const handler = apiHandler(async (event, context) => {
const user = await createUser(event.body);
return { statusCode: 201, body: user };
});
// Automatic error handling, logging, and response formattingYour users — and your support team — deserve better than ValidationError at path[0].nested.field:
import { handleSchemaValidation, HumanReadableSchemaError } from '@smooai/utils';
const userSchema = z.object({
email: z.string().email(),
phone: validateAndTransformPhoneNumber,
age: z.number().min(18),
});
try {
const user = handleSchemaValidation(userSchema, data);
// user.phone is guaranteed to be E.164 format: +12125551234
} catch (error) {
if (error instanceof HumanReadableSchemaError) {
console.log(error.humanReadableMessage);
// "Email must be a valid email address. Phone must be a valid phone number. Age must be at least 18."
}
}Because Content-Type, content-type, and CONTENT-TYPE should all just work:
import { CaseInsensitiveMap } from '@smooai/utils';
const headers = new CaseInsensitiveMap([
['Content-Type', 'application/json'],
['X-API-KEY', 'secret'],
]);
headers.get('content-type'); // 'application/json'
headers.has('X-Api-Key'); // true
headers.get('CONTENT-TYPE'); // 'application/json'Set up a fully configured API in one line:
import { createAwsLambdaHonoApp } from '@smooai/utils';
const app = createAwsLambdaHonoApp();
app.get('/health', (c) => c.json({ status: 'healthy' }));
app.post('/users', async (c) => {
// Automatic request ID tracking
// Built-in error handling
// Pretty JSON in development
const user = await createUser(await c.req.json());
return c.json(user, 201);
});
export const handler = handle(app);Find configuration files without hardcoding paths:
import { findFile } from '@smooai/utils';
// Searches up the directory tree until it finds the file
const configPath = await findFile('smoo.config.json');
const packageJson = await findFile('package.json');
// Perfect for:
// - Finding project root
// - Loading environment-specific configs
// - Locating test fixturesimport { isRunningInProd, isRunningLocally } from '@smooai/utils';
if (isRunningLocally()) {
// Enable debug logging
// Use local database
// Show detailed errors
}
if (isRunningInProd()) {
// Use production services
// Enable monitoring
// Sanitize error messages
}Transform cryptic errors into actionable messages:
import { ApiError, errorHandler } from '@smooai/utils';
const processPayment = errorHandler(
async (orderId: string) => {
// Throws ApiError with 404 status
if (!order) throw new ApiError('Order not found', 404);
// Validation errors become 400s with details
const validated = handleSchemaValidation(schema, data);
// Unexpected errors are logged and sanitized
return await chargeCard(order);
},
{
logger: console,
onError: (error) => notifyOps(error),
},
);import { sleep } from '@smooai/utils';
// Rate limiting made easy
for (const batch of batches) {
await processBatch(batch);
await sleep(1000); // Wait 1 second between batches
}
// Retry with exponential backoff
async function retryWithBackoff(fn, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (error) {
if (i === attempts - 1) throw error;
await sleep(Math.pow(2, i) * 1000);
}
}
}import { validateAndTransformPhoneNumber } from '@smooai/utils';
// Accepts multiple formats, outputs E.164
const phoneSchema = z.object({
phone: validateAndTransformPhoneNumber,
});
phoneSchema.parse({ phone: '(212) 555-1234' });
// { phone: '+12125551234' }
phoneSchema.parse({ phone: '+44 20 7946 0958' });
// { phone: '+442079460958' }
phoneSchema.parse({ phone: '555-1234' });
// Throws: "Phone must be a valid phone number"Every utility in this package is:
- 🔒 Type-safe — full TypeScript support with strict types
- ⚡ Performance tested — optimized for real-world usage
- 📊 Battle-tested — used in production at Smoo AI
- 📚 Well-documented — clear examples and use cases
- 🔄 Maintained — regular updates and improvements
pnpm testpnpm lint@smooai/utils is built and open-sourced by Smoo AI — the AI-powered business platform with AI built into every product: CRM, customer support, campaigns, field service, observability, and developer tools.
- 🧰 More open source from Smoo AI — smoo.ai/open-source
- 🧩 Sibling packages — @smooai/logger, @smooai/fetch, @smooai/file, @smooai/config
We're still developing our contribution processes. If you'd like to contribute or have questions, reach out through the contact information below.
MIT — see LICENSE.
Brent Rager
Smoo GitHub: https://github.com/SmooAI
Built by Smoo AI — AI built into every product.
