Thin MongoDB ODM built for Standard Schema
mongodb
zod
deno
1import type { z } from "@zod/zod";
2
3// Type for Zod validation issues
4type ValidationIssue = z.ZodIssue;
5
6/**
7 * Base error class for all Nozzle errors
8 */
9export class NozzleError extends Error {
10 constructor(message: string) {
11 super(message);
12 this.name = this.constructor.name;
13 // Maintains proper stack trace for where error was thrown (only available on V8)
14 if (Error.captureStackTrace) {
15 Error.captureStackTrace(this, this.constructor);
16 }
17 }
18}
19
20/**
21 * Validation error with structured issue details
22 * Thrown when data fails schema validation
23 */
24export class ValidationError extends NozzleError {
25 public readonly issues: ValidationIssue[];
26 public readonly operation: "insert" | "update" | "replace";
27
28 constructor(issues: ValidationIssue[], operation: "insert" | "update" | "replace") {
29 const message = ValidationError.formatIssues(issues);
30 super(`Validation failed on ${operation}: ${message}`);
31 this.issues = issues;
32 this.operation = operation;
33 }
34
35 private static formatIssues(issues: ValidationIssue[]): string {
36 return issues.map(issue => {
37 const path = issue.path.join('.');
38 return `${path || 'root'}: ${issue.message}`;
39 }).join('; ');
40 }
41
42 /**
43 * Get validation errors grouped by field
44 */
45 public getFieldErrors(): Record<string, string[]> {
46 const fieldErrors: Record<string, string[]> = {};
47 for (const issue of this.issues) {
48 const field = issue.path.join('.') || 'root';
49 if (!fieldErrors[field]) {
50 fieldErrors[field] = [];
51 }
52 fieldErrors[field].push(issue.message);
53 }
54 return fieldErrors;
55 }
56}
57
58/**
59 * Connection error
60 * Thrown when database connection fails or is not established
61 */
62export class ConnectionError extends NozzleError {
63 public readonly uri?: string;
64
65 constructor(message: string, uri?: string) {
66 super(message);
67 this.uri = uri;
68 }
69}
70
71/**
72 * Configuration error
73 * Thrown when invalid configuration options are provided
74 */
75export class ConfigurationError extends NozzleError {
76 public readonly option?: string;
77
78 constructor(message: string, option?: string) {
79 super(message);
80 this.option = option;
81 }
82}
83
84/**
85 * Document not found error
86 * Thrown when a required document is not found
87 */
88export class DocumentNotFoundError extends NozzleError {
89 public readonly query: unknown;
90 public readonly collection: string;
91
92 constructor(collection: string, query: unknown) {
93 super(`Document not found in collection '${collection}'`);
94 this.collection = collection;
95 this.query = query;
96 }
97}
98
99/**
100 * Operation error
101 * Thrown when a database operation fails
102 */
103export class OperationError extends NozzleError {
104 public readonly operation: string;
105 public readonly collection?: string;
106 public override readonly cause?: Error;
107
108 constructor(operation: string, message: string, collection?: string, cause?: Error) {
109 super(`${operation} operation failed: ${message}`);
110 this.operation = operation;
111 this.collection = collection;
112 this.cause = cause;
113 }
114}
115
116/**
117 * Async validation not supported error
118 * Thrown when async validation is attempted
119 */
120export class AsyncValidationError extends NozzleError {
121 constructor() {
122 super(
123 "Async validation is not currently supported. " +
124 "Please use synchronous validation schemas."
125 );
126 }
127}