@constructive-io/graphql-server 3.1.1 → 4.0.0
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.
- package/errors/404-message.js +1 -1
- package/errors/api-errors.d.ts +200 -0
- package/errors/api-errors.js +276 -0
- package/esm/errors/404-message.js +1 -1
- package/esm/errors/api-errors.js +261 -0
- package/esm/index.js +2 -0
- package/esm/middleware/api.js +355 -277
- package/esm/middleware/auth.js +25 -7
- package/esm/middleware/error-handler.js +86 -0
- package/esm/middleware/favicon.js +12 -0
- package/esm/middleware/graphile.js +149 -64
- package/esm/options.js +232 -0
- package/esm/schema.js +24 -11
- package/esm/server.js +41 -5
- package/index.d.ts +1 -0
- package/index.js +2 -0
- package/middleware/api.d.ts +3 -15
- package/middleware/api.js +359 -283
- package/middleware/auth.js +25 -7
- package/middleware/error-handler.d.ts +4 -0
- package/middleware/error-handler.js +94 -0
- package/middleware/favicon.d.ts +2 -0
- package/middleware/favicon.js +16 -0
- package/middleware/graphile.d.ts +14 -0
- package/middleware/graphile.js +149 -64
- package/options.d.ts +131 -0
- package/options.js +244 -0
- package/package.json +23 -24
- package/schema.d.ts +2 -2
- package/schema.js +23 -10
- package/server.d.ts +24 -2
- package/server.js +39 -3
- package/codegen/orm/client.d.ts +0 -55
- package/codegen/orm/client.js +0 -75
- package/codegen/orm/index.d.ts +0 -36
- package/codegen/orm/index.js +0 -59
- package/codegen/orm/input-types.d.ts +0 -20140
- package/codegen/orm/input-types.js +0 -2
- package/codegen/orm/models/api.d.ts +0 -42
- package/codegen/orm/models/api.js +0 -76
- package/codegen/orm/models/domain.d.ts +0 -42
- package/codegen/orm/models/domain.js +0 -76
- package/codegen/orm/models/index.d.ts +0 -7
- package/codegen/orm/models/index.js +0 -12
- package/codegen/orm/mutation/index.d.ts +0 -7
- package/codegen/orm/mutation/index.js +0 -7
- package/codegen/orm/query/index.d.ts +0 -20
- package/codegen/orm/query/index.js +0 -24
- package/codegen/orm/query-builder.d.ts +0 -81
- package/codegen/orm/query-builder.js +0 -496
- package/codegen/orm/select-types.d.ts +0 -83
- package/codegen/orm/select-types.js +0 -7
- package/codegen/orm/types.d.ts +0 -6
- package/codegen/orm/types.js +0 -23
- package/esm/codegen/orm/client.js +0 -70
- package/esm/codegen/orm/index.js +0 -39
- package/esm/codegen/orm/input-types.js +0 -1
- package/esm/codegen/orm/models/api.js +0 -72
- package/esm/codegen/orm/models/domain.js +0 -72
- package/esm/codegen/orm/models/index.js +0 -7
- package/esm/codegen/orm/mutation/index.js +0 -4
- package/esm/codegen/orm/query/index.js +0 -21
- package/esm/codegen/orm/query-builder.js +0 -452
- package/esm/codegen/orm/select-types.js +0 -6
- package/esm/codegen/orm/types.js +0 -7
- package/esm/middleware/gql.js +0 -116
- package/esm/plugins/PublicKeySignature.js +0 -114
- package/esm/scripts/codegen-schema.js +0 -71
- package/esm/scripts/create-bucket.js +0 -40
- package/middleware/gql.d.ts +0 -164
- package/middleware/gql.js +0 -121
- package/plugins/PublicKeySignature.d.ts +0 -11
- package/plugins/PublicKeySignature.js +0 -121
- package/scripts/codegen-schema.d.ts +0 -1
- package/scripts/codegen-schema.js +0 -76
- package/scripts/create-bucket.d.ts +0 -1
- package/scripts/create-bucket.js +0 -42
package/errors/404-message.js
CHANGED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed API Error System
|
|
3
|
+
*
|
|
4
|
+
* Provides strongly-typed error hierarchy for the GraphQL server with:
|
|
5
|
+
* - Consistent error classification
|
|
6
|
+
* - HTTP status mapping
|
|
7
|
+
* - Serialization for API responses and logging
|
|
8
|
+
*
|
|
9
|
+
* @module errors/api-errors
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Centralized error code definitions for external use and type safety.
|
|
13
|
+
* Use these constants instead of string literals for error code comparisons.
|
|
14
|
+
*/
|
|
15
|
+
export declare const ErrorCodes: {
|
|
16
|
+
readonly DOMAIN_NOT_FOUND: "DOMAIN_NOT_FOUND";
|
|
17
|
+
readonly API_NOT_FOUND: "API_NOT_FOUND";
|
|
18
|
+
readonly NO_VALID_SCHEMAS: "NO_VALID_SCHEMAS";
|
|
19
|
+
readonly SCHEMA_INVALID: "SCHEMA_INVALID";
|
|
20
|
+
readonly SCHEMA_ACCESS_DENIED: "SCHEMA_ACCESS_DENIED";
|
|
21
|
+
readonly HANDLER_ERROR: "HANDLER_ERROR";
|
|
22
|
+
readonly DATABASE_CONNECTION_ERROR: "DATABASE_CONNECTION_ERROR";
|
|
23
|
+
readonly AMBIGUOUS_TENANT: "AMBIGUOUS_TENANT";
|
|
24
|
+
readonly ADMIN_AUTH_REQUIRED: "ADMIN_AUTH_REQUIRED";
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Type alias for valid error codes.
|
|
28
|
+
* Derived from the ErrorCodes constant for type safety.
|
|
29
|
+
*/
|
|
30
|
+
export type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
|
|
31
|
+
/**
|
|
32
|
+
* Base class for all API errors.
|
|
33
|
+
*
|
|
34
|
+
* Provides consistent structure for error handling including:
|
|
35
|
+
* - Machine-readable error codes
|
|
36
|
+
* - HTTP status code mapping
|
|
37
|
+
* - Optional debugging context
|
|
38
|
+
* - JSON serialization for API responses
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* throw new ApiError('CUSTOM_ERROR', 400, 'Something went wrong', { detail: 'info' });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare class ApiError extends Error {
|
|
46
|
+
readonly code: string;
|
|
47
|
+
readonly statusCode: number;
|
|
48
|
+
readonly context?: Record<string, unknown>;
|
|
49
|
+
constructor(code: string, statusCode: number, message: string, context?: Record<string, unknown>);
|
|
50
|
+
/**
|
|
51
|
+
* Serializes the error for API responses and structured logging.
|
|
52
|
+
*
|
|
53
|
+
* @returns Object with error details suitable for JSON serialization
|
|
54
|
+
*/
|
|
55
|
+
toJSON(): Record<string, unknown>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Thrown when a domain lookup fails to find a matching API.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* throw new DomainNotFoundError('example.com', 'api');
|
|
63
|
+
* // Results in: "No API configured for domain: api.example.com"
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare class DomainNotFoundError extends ApiError {
|
|
67
|
+
constructor(domain: string, subdomain: string | null);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Thrown when a specific API cannot be found by its ID.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* throw new ApiNotFoundError('api-123');
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare class ApiNotFoundError extends ApiError {
|
|
78
|
+
constructor(apiId: string);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Thrown when no valid schemas are found for an API.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* throw new NoValidSchemasError('api-123');
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export declare class NoValidSchemasError extends ApiError {
|
|
89
|
+
constructor(apiId: string);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Thrown when schema validation fails.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* throw new SchemaValidationError('Invalid schema structure', { field: 'name' });
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export declare class SchemaValidationError extends ApiError {
|
|
100
|
+
constructor(message: string, context?: Record<string, unknown>);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Thrown when a request handler cannot be created.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* throw new HandlerCreationError('Failed to create PostGraphile handler', { reason: 'timeout' });
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export declare class HandlerCreationError extends ApiError {
|
|
111
|
+
constructor(message: string, context?: Record<string, unknown>);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Thrown when the database connection fails.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* throw new DatabaseConnectionError('Connection timeout', { host: 'db.example.com' });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export declare class DatabaseConnectionError extends ApiError {
|
|
122
|
+
constructor(message: string, context?: Record<string, unknown>);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Thrown when a tenant attempts to access schemas they do not own.
|
|
126
|
+
* Returns 403 Forbidden to indicate the schemas exist but access is denied.
|
|
127
|
+
*
|
|
128
|
+
* Security Note: This error intentionally does not reveal which specific
|
|
129
|
+
* schemas exist to prevent information disclosure.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* throw new SchemaAccessDeniedError(['schema1', 'schema2'], 'db-123');
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export declare class SchemaAccessDeniedError extends ApiError {
|
|
137
|
+
constructor(schemas: string[], databaseId: string);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Thrown when domain resolution is ambiguous (multiple APIs match).
|
|
141
|
+
* This is a security concern as it indicates potential misconfiguration
|
|
142
|
+
* that could lead to unpredictable tenant routing.
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* throw new AmbiguousTenantError('example.com', 'api', 2);
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
export declare class AmbiguousTenantError extends ApiError {
|
|
150
|
+
constructor(domain: string, subdomain: string | null, matchCount: number);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Thrown when admin authentication is required but not provided or invalid.
|
|
154
|
+
* Used for private API endpoints that require explicit admin credentials.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* throw new AdminAuthRequiredError('Missing authorization header');
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export declare class AdminAuthRequiredError extends ApiError {
|
|
162
|
+
constructor(reason: string);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Check if an error is an instance of ApiError or any of its subclasses.
|
|
166
|
+
*
|
|
167
|
+
* @param error - The value to check
|
|
168
|
+
* @returns True if error is an ApiError instance
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* try {
|
|
173
|
+
* await resolveApi(req);
|
|
174
|
+
* } catch (error) {
|
|
175
|
+
* if (isApiError(error)) {
|
|
176
|
+
* // TypeScript knows error has code, statusCode, context
|
|
177
|
+
* console.log(error.code);
|
|
178
|
+
* res.status(error.statusCode).json(error.toJSON());
|
|
179
|
+
* }
|
|
180
|
+
* }
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
export declare function isApiError(error: unknown): error is ApiError;
|
|
184
|
+
/**
|
|
185
|
+
* Check if an error has a specific error code.
|
|
186
|
+
* Returns false for non-ApiError values.
|
|
187
|
+
*
|
|
188
|
+
* @param error - The value to check
|
|
189
|
+
* @param code - The error code to match against
|
|
190
|
+
* @returns True if error is an ApiError with the specified code
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* ```typescript
|
|
194
|
+
* if (hasErrorCode(error, ErrorCodes.DOMAIN_NOT_FOUND)) {
|
|
195
|
+
* // Handle domain not found specifically
|
|
196
|
+
* logDomainMisconfiguration(error.context?.fullDomain);
|
|
197
|
+
* }
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
export declare function hasErrorCode(error: unknown, code: string): error is ApiError;
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Typed API Error System
|
|
4
|
+
*
|
|
5
|
+
* Provides strongly-typed error hierarchy for the GraphQL server with:
|
|
6
|
+
* - Consistent error classification
|
|
7
|
+
* - HTTP status mapping
|
|
8
|
+
* - Serialization for API responses and logging
|
|
9
|
+
*
|
|
10
|
+
* @module errors/api-errors
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.AdminAuthRequiredError = exports.AmbiguousTenantError = exports.SchemaAccessDeniedError = exports.DatabaseConnectionError = exports.HandlerCreationError = exports.SchemaValidationError = exports.NoValidSchemasError = exports.ApiNotFoundError = exports.DomainNotFoundError = exports.ApiError = exports.ErrorCodes = void 0;
|
|
14
|
+
exports.isApiError = isApiError;
|
|
15
|
+
exports.hasErrorCode = hasErrorCode;
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Error Codes Constant
|
|
18
|
+
// =============================================================================
|
|
19
|
+
/**
|
|
20
|
+
* Centralized error code definitions for external use and type safety.
|
|
21
|
+
* Use these constants instead of string literals for error code comparisons.
|
|
22
|
+
*/
|
|
23
|
+
exports.ErrorCodes = {
|
|
24
|
+
DOMAIN_NOT_FOUND: 'DOMAIN_NOT_FOUND',
|
|
25
|
+
API_NOT_FOUND: 'API_NOT_FOUND',
|
|
26
|
+
NO_VALID_SCHEMAS: 'NO_VALID_SCHEMAS',
|
|
27
|
+
SCHEMA_INVALID: 'SCHEMA_INVALID',
|
|
28
|
+
SCHEMA_ACCESS_DENIED: 'SCHEMA_ACCESS_DENIED',
|
|
29
|
+
HANDLER_ERROR: 'HANDLER_ERROR',
|
|
30
|
+
DATABASE_CONNECTION_ERROR: 'DATABASE_CONNECTION_ERROR',
|
|
31
|
+
AMBIGUOUS_TENANT: 'AMBIGUOUS_TENANT',
|
|
32
|
+
ADMIN_AUTH_REQUIRED: 'ADMIN_AUTH_REQUIRED',
|
|
33
|
+
};
|
|
34
|
+
// =============================================================================
|
|
35
|
+
// Base Error Class
|
|
36
|
+
// =============================================================================
|
|
37
|
+
/**
|
|
38
|
+
* Base class for all API errors.
|
|
39
|
+
*
|
|
40
|
+
* Provides consistent structure for error handling including:
|
|
41
|
+
* - Machine-readable error codes
|
|
42
|
+
* - HTTP status code mapping
|
|
43
|
+
* - Optional debugging context
|
|
44
|
+
* - JSON serialization for API responses
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* throw new ApiError('CUSTOM_ERROR', 400, 'Something went wrong', { detail: 'info' });
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
class ApiError extends Error {
|
|
52
|
+
code;
|
|
53
|
+
statusCode;
|
|
54
|
+
context;
|
|
55
|
+
constructor(code, statusCode, message, context) {
|
|
56
|
+
super(message);
|
|
57
|
+
this.code = code;
|
|
58
|
+
this.statusCode = statusCode;
|
|
59
|
+
this.context = context;
|
|
60
|
+
this.name = 'ApiError';
|
|
61
|
+
// Ensure instanceof checks work correctly with ES5 transpilation
|
|
62
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
63
|
+
// Capture stack trace for V8 engines (Node.js)
|
|
64
|
+
// Points to error origin rather than base class constructor
|
|
65
|
+
if (Error.captureStackTrace) {
|
|
66
|
+
Error.captureStackTrace(this, this.constructor);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Serializes the error for API responses and structured logging.
|
|
71
|
+
*
|
|
72
|
+
* @returns Object with error details suitable for JSON serialization
|
|
73
|
+
*/
|
|
74
|
+
toJSON() {
|
|
75
|
+
return {
|
|
76
|
+
name: this.name,
|
|
77
|
+
code: this.code,
|
|
78
|
+
message: this.message,
|
|
79
|
+
statusCode: this.statusCode,
|
|
80
|
+
...(this.context && { context: this.context }),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.ApiError = ApiError;
|
|
85
|
+
// =============================================================================
|
|
86
|
+
// Error Subclasses
|
|
87
|
+
// =============================================================================
|
|
88
|
+
/**
|
|
89
|
+
* Thrown when a domain lookup fails to find a matching API.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* throw new DomainNotFoundError('example.com', 'api');
|
|
94
|
+
* // Results in: "No API configured for domain: api.example.com"
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
class DomainNotFoundError extends ApiError {
|
|
98
|
+
constructor(domain, subdomain) {
|
|
99
|
+
const fullDomain = subdomain ? `${subdomain}.${domain}` : domain;
|
|
100
|
+
super(exports.ErrorCodes.DOMAIN_NOT_FOUND, 404, `No API configured for domain: ${fullDomain}`, { domain, subdomain, fullDomain });
|
|
101
|
+
this.name = 'DomainNotFoundError';
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.DomainNotFoundError = DomainNotFoundError;
|
|
105
|
+
/**
|
|
106
|
+
* Thrown when a specific API cannot be found by its ID.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* throw new ApiNotFoundError('api-123');
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
class ApiNotFoundError extends ApiError {
|
|
114
|
+
constructor(apiId) {
|
|
115
|
+
super(exports.ErrorCodes.API_NOT_FOUND, 404, `API not found: ${apiId}`, { apiId });
|
|
116
|
+
this.name = 'ApiNotFoundError';
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
exports.ApiNotFoundError = ApiNotFoundError;
|
|
120
|
+
/**
|
|
121
|
+
* Thrown when no valid schemas are found for an API.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* throw new NoValidSchemasError('api-123');
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
class NoValidSchemasError extends ApiError {
|
|
129
|
+
constructor(apiId) {
|
|
130
|
+
super(exports.ErrorCodes.NO_VALID_SCHEMAS, 404, `No valid schemas found for API: ${apiId}`, { apiId });
|
|
131
|
+
this.name = 'NoValidSchemasError';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.NoValidSchemasError = NoValidSchemasError;
|
|
135
|
+
/**
|
|
136
|
+
* Thrown when schema validation fails.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* throw new SchemaValidationError('Invalid schema structure', { field: 'name' });
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
class SchemaValidationError extends ApiError {
|
|
144
|
+
constructor(message, context) {
|
|
145
|
+
super(exports.ErrorCodes.SCHEMA_INVALID, 400, message, context);
|
|
146
|
+
this.name = 'SchemaValidationError';
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
exports.SchemaValidationError = SchemaValidationError;
|
|
150
|
+
/**
|
|
151
|
+
* Thrown when a request handler cannot be created.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* throw new HandlerCreationError('Failed to create PostGraphile handler', { reason: 'timeout' });
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
class HandlerCreationError extends ApiError {
|
|
159
|
+
constructor(message, context) {
|
|
160
|
+
super(exports.ErrorCodes.HANDLER_ERROR, 500, message, context);
|
|
161
|
+
this.name = 'HandlerCreationError';
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.HandlerCreationError = HandlerCreationError;
|
|
165
|
+
/**
|
|
166
|
+
* Thrown when the database connection fails.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```typescript
|
|
170
|
+
* throw new DatabaseConnectionError('Connection timeout', { host: 'db.example.com' });
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
class DatabaseConnectionError extends ApiError {
|
|
174
|
+
constructor(message, context) {
|
|
175
|
+
super(exports.ErrorCodes.DATABASE_CONNECTION_ERROR, 503, message, context);
|
|
176
|
+
this.name = 'DatabaseConnectionError';
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
exports.DatabaseConnectionError = DatabaseConnectionError;
|
|
180
|
+
/**
|
|
181
|
+
* Thrown when a tenant attempts to access schemas they do not own.
|
|
182
|
+
* Returns 403 Forbidden to indicate the schemas exist but access is denied.
|
|
183
|
+
*
|
|
184
|
+
* Security Note: This error intentionally does not reveal which specific
|
|
185
|
+
* schemas exist to prevent information disclosure.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* throw new SchemaAccessDeniedError(['schema1', 'schema2'], 'db-123');
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
class SchemaAccessDeniedError extends ApiError {
|
|
193
|
+
constructor(schemas, databaseId) {
|
|
194
|
+
super(exports.ErrorCodes.SCHEMA_ACCESS_DENIED, 403, `Access denied: requested schemas are not associated with tenant`, { schemas, databaseId });
|
|
195
|
+
this.name = 'SchemaAccessDeniedError';
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
exports.SchemaAccessDeniedError = SchemaAccessDeniedError;
|
|
199
|
+
/**
|
|
200
|
+
* Thrown when domain resolution is ambiguous (multiple APIs match).
|
|
201
|
+
* This is a security concern as it indicates potential misconfiguration
|
|
202
|
+
* that could lead to unpredictable tenant routing.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```typescript
|
|
206
|
+
* throw new AmbiguousTenantError('example.com', 'api', 2);
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
class AmbiguousTenantError extends ApiError {
|
|
210
|
+
constructor(domain, subdomain, matchCount) {
|
|
211
|
+
const fullDomain = subdomain ? `${subdomain}.${domain}` : domain;
|
|
212
|
+
super(exports.ErrorCodes.AMBIGUOUS_TENANT, 500, `Ambiguous tenant resolution: multiple APIs (${matchCount}) match domain ${fullDomain}`, { domain, subdomain, fullDomain, matchCount });
|
|
213
|
+
this.name = 'AmbiguousTenantError';
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
exports.AmbiguousTenantError = AmbiguousTenantError;
|
|
217
|
+
/**
|
|
218
|
+
* Thrown when admin authentication is required but not provided or invalid.
|
|
219
|
+
* Used for private API endpoints that require explicit admin credentials.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```typescript
|
|
223
|
+
* throw new AdminAuthRequiredError('Missing authorization header');
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
class AdminAuthRequiredError extends ApiError {
|
|
227
|
+
constructor(reason) {
|
|
228
|
+
super(exports.ErrorCodes.ADMIN_AUTH_REQUIRED, 401, `Admin authentication required: ${reason}`, { reason });
|
|
229
|
+
this.name = 'AdminAuthRequiredError';
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
exports.AdminAuthRequiredError = AdminAuthRequiredError;
|
|
233
|
+
// =============================================================================
|
|
234
|
+
// Type Guards
|
|
235
|
+
// =============================================================================
|
|
236
|
+
/**
|
|
237
|
+
* Check if an error is an instance of ApiError or any of its subclasses.
|
|
238
|
+
*
|
|
239
|
+
* @param error - The value to check
|
|
240
|
+
* @returns True if error is an ApiError instance
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```typescript
|
|
244
|
+
* try {
|
|
245
|
+
* await resolveApi(req);
|
|
246
|
+
* } catch (error) {
|
|
247
|
+
* if (isApiError(error)) {
|
|
248
|
+
* // TypeScript knows error has code, statusCode, context
|
|
249
|
+
* console.log(error.code);
|
|
250
|
+
* res.status(error.statusCode).json(error.toJSON());
|
|
251
|
+
* }
|
|
252
|
+
* }
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
function isApiError(error) {
|
|
256
|
+
return error instanceof ApiError;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Check if an error has a specific error code.
|
|
260
|
+
* Returns false for non-ApiError values.
|
|
261
|
+
*
|
|
262
|
+
* @param error - The value to check
|
|
263
|
+
* @param code - The error code to match against
|
|
264
|
+
* @returns True if error is an ApiError with the specified code
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```typescript
|
|
268
|
+
* if (hasErrorCode(error, ErrorCodes.DOMAIN_NOT_FOUND)) {
|
|
269
|
+
* // Handle domain not found specifically
|
|
270
|
+
* logDomainMisconfiguration(error.context?.fullDomain);
|
|
271
|
+
* }
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
function hasErrorCode(error, code) {
|
|
275
|
+
return isApiError(error) && error.code === code;
|
|
276
|
+
}
|