@cosmneo/onion-lasagna 0.3.0 → 0.4.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/dist/{chunk-XWKHOLIP.js → chunk-4HMXTGHK.js} +2 -2
- package/dist/chunk-4RFWJ5XZ.js +192 -0
- package/dist/chunk-4RFWJ5XZ.js.map +1 -0
- package/dist/{chunk-4BVOLXDJ.js → chunk-4YBAV6LZ.js} +2 -2
- package/dist/chunk-ANLXZHUS.js +230 -0
- package/dist/chunk-ANLXZHUS.js.map +1 -0
- package/dist/chunk-BG2FY27M.js +36 -0
- package/dist/chunk-BG2FY27M.js.map +1 -0
- package/dist/chunk-FEY2GSVT.js +1 -0
- package/dist/chunk-FEY2GSVT.js.map +1 -0
- package/dist/{chunk-2BVCU32G.js → chunk-HNEAH6OZ.js} +121 -2
- package/dist/chunk-HNEAH6OZ.js.map +1 -0
- package/dist/chunk-NQMFWI6Q.js +1 -0
- package/dist/chunk-NQMFWI6Q.js.map +1 -0
- package/dist/chunk-TZRBETT3.js +127 -0
- package/dist/chunk-TZRBETT3.js.map +1 -0
- package/dist/{chunk-MF2JDREK.js → chunk-UNVB4INM.js} +1 -1
- package/dist/{chunk-MF2JDREK.js.map → chunk-UNVB4INM.js.map} +1 -1
- package/dist/chunk-VBG3UYQR.js +119 -0
- package/dist/chunk-VBG3UYQR.js.map +1 -0
- package/dist/events/index.js +3 -4
- package/dist/events/server/index.js +3 -4
- package/dist/events/shared/index.js +2 -3
- package/dist/graphql/field/index.cjs +189 -0
- package/dist/graphql/field/index.cjs.map +1 -0
- package/dist/graphql/field/index.d.cts +214 -0
- package/dist/graphql/field/index.d.ts +214 -0
- package/dist/graphql/field/index.js +25 -0
- package/dist/graphql/field/index.js.map +1 -0
- package/dist/graphql/index.cjs +1148 -0
- package/dist/graphql/index.cjs.map +1 -0
- package/dist/graphql/index.d.cts +8 -0
- package/dist/graphql/index.d.ts +8 -0
- package/dist/graphql/index.js +49 -0
- package/dist/graphql/index.js.map +1 -0
- package/dist/graphql/sdl/index.cjs +241 -0
- package/dist/graphql/sdl/index.cjs.map +1 -0
- package/dist/graphql/sdl/index.d.cts +77 -0
- package/dist/graphql/sdl/index.d.ts +77 -0
- package/dist/graphql/sdl/index.js +8 -0
- package/dist/graphql/sdl/index.js.map +1 -0
- package/dist/graphql/server/index.cjs +505 -0
- package/dist/graphql/server/index.cjs.map +1 -0
- package/dist/graphql/server/index.d.cts +268 -0
- package/dist/graphql/server/index.d.ts +268 -0
- package/dist/graphql/server/index.js +15 -0
- package/dist/graphql/server/index.js.map +1 -0
- package/dist/graphql/shared/index.cjs +586 -0
- package/dist/graphql/shared/index.cjs.map +1 -0
- package/dist/graphql/shared/index.d.cts +82 -0
- package/dist/graphql/shared/index.d.ts +82 -0
- package/dist/graphql/shared/index.js +16 -0
- package/dist/graphql/shared/index.js.map +1 -0
- package/dist/http/index.cjs.map +1 -1
- package/dist/http/index.js +2 -3
- package/dist/http/route/index.cjs.map +1 -1
- package/dist/http/route/index.d.cts +4 -0
- package/dist/http/route/index.d.ts +4 -0
- package/dist/http/route/index.js +1 -1
- package/dist/http/shared/index.js +1 -2
- package/dist/index.cjs +672 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +43 -1
- package/dist/index.js.map +1 -1
- package/dist/schema-definition.type-C9PntBVc.d.cts +166 -0
- package/dist/schema-definition.type-CuhQLDr0.d.ts +166 -0
- package/package.json +31 -1
- package/dist/chunk-2BVCU32G.js.map +0 -1
- package/dist/chunk-H5TNDC5U.js +0 -138
- package/dist/chunk-H5TNDC5U.js.map +0 -1
- /package/dist/{chunk-XWKHOLIP.js.map → chunk-4HMXTGHK.js.map} +0 -0
- /package/dist/{chunk-4BVOLXDJ.js.map → chunk-4YBAV6LZ.js.map} +0 -0
|
@@ -0,0 +1,1148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/presentation/graphql/index.ts
|
|
21
|
+
var graphql_exports = {};
|
|
22
|
+
__export(graphql_exports, {
|
|
23
|
+
collectFields: () => collectFields,
|
|
24
|
+
defineGraphQLSchema: () => defineGraphQLSchema,
|
|
25
|
+
defineMutation: () => defineMutation,
|
|
26
|
+
defineQuery: () => defineQuery,
|
|
27
|
+
defineSubscription: () => defineSubscription,
|
|
28
|
+
generateFieldId: () => generateFieldId,
|
|
29
|
+
generateGraphQLSDL: () => generateGraphQLSDL,
|
|
30
|
+
getGraphQLErrorCode: () => getGraphQLErrorCode,
|
|
31
|
+
graphqlRoutes: () => graphqlRoutes,
|
|
32
|
+
isFieldDefinition: () => isFieldDefinition,
|
|
33
|
+
isSchemaDefinition: () => isSchemaDefinition,
|
|
34
|
+
isSimpleGraphQLHandlerConfig: () => isSimpleGraphQLHandlerConfig,
|
|
35
|
+
mapErrorToGraphQLError: () => mapErrorToGraphQLError,
|
|
36
|
+
mergeGraphQLSchemas: () => mergeGraphQLSchemas,
|
|
37
|
+
shouldMaskGraphQLError: () => shouldMaskGraphQLError
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(graphql_exports);
|
|
40
|
+
|
|
41
|
+
// src/presentation/graphql/field/define-field.ts
|
|
42
|
+
function createFieldDefinition(operation, input) {
|
|
43
|
+
const definition = {
|
|
44
|
+
operation,
|
|
45
|
+
input: input.input ?? void 0,
|
|
46
|
+
output: input.output ?? void 0,
|
|
47
|
+
context: input.context ?? void 0,
|
|
48
|
+
docs: {
|
|
49
|
+
summary: input.docs?.summary,
|
|
50
|
+
description: input.docs?.description,
|
|
51
|
+
tags: input.docs?.tags,
|
|
52
|
+
deprecated: input.docs?.deprecated ?? false,
|
|
53
|
+
deprecationReason: input.docs?.deprecationReason
|
|
54
|
+
},
|
|
55
|
+
_isGraphQLField: true,
|
|
56
|
+
_types: void 0
|
|
57
|
+
};
|
|
58
|
+
return Object.freeze(definition);
|
|
59
|
+
}
|
|
60
|
+
function defineQuery(config = {}) {
|
|
61
|
+
return createFieldDefinition("query", config);
|
|
62
|
+
}
|
|
63
|
+
function defineMutation(config = {}) {
|
|
64
|
+
return createFieldDefinition("mutation", config);
|
|
65
|
+
}
|
|
66
|
+
function defineSubscription(config = {}) {
|
|
67
|
+
return createFieldDefinition("subscription", config);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/presentation/graphql/field/types/schema-definition.type.ts
|
|
71
|
+
function isFieldDefinition(value) {
|
|
72
|
+
return typeof value === "object" && value !== null && "_isGraphQLField" in value && value._isGraphQLField === true;
|
|
73
|
+
}
|
|
74
|
+
function isSchemaDefinition(value) {
|
|
75
|
+
return typeof value === "object" && value !== null && "_isGraphQLSchema" in value && value._isGraphQLSchema === true;
|
|
76
|
+
}
|
|
77
|
+
function collectFields(config, basePath = "") {
|
|
78
|
+
const fields = [];
|
|
79
|
+
for (const [key, value] of Object.entries(config)) {
|
|
80
|
+
const fullKey = basePath ? `${basePath}.${key}` : key;
|
|
81
|
+
if (isFieldDefinition(value)) {
|
|
82
|
+
fields.push({ key: fullKey, field: value });
|
|
83
|
+
} else if (isSchemaDefinition(value)) {
|
|
84
|
+
fields.push(...collectFields(value.fields, fullKey));
|
|
85
|
+
} else if (typeof value === "object" && value !== null) {
|
|
86
|
+
fields.push(...collectFields(value, fullKey));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return fields;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/presentation/graphql/field/define-schema.ts
|
|
93
|
+
function defineGraphQLSchema(fields, options) {
|
|
94
|
+
const defaults = options?.defaults;
|
|
95
|
+
const processedFields = defaults?.context || defaults?.tags ? applySchemaDefaults(fields, defaults) : fields;
|
|
96
|
+
const definition = {
|
|
97
|
+
fields: processedFields,
|
|
98
|
+
defaults,
|
|
99
|
+
_isGraphQLSchema: true
|
|
100
|
+
};
|
|
101
|
+
return deepFreeze(definition);
|
|
102
|
+
}
|
|
103
|
+
function applySchemaDefaults(config, defaults) {
|
|
104
|
+
const result = {};
|
|
105
|
+
for (const [key, value] of Object.entries(config)) {
|
|
106
|
+
if (isFieldDefinition(value)) {
|
|
107
|
+
result[key] = applyDefaultsToField(value, defaults);
|
|
108
|
+
} else if (isSchemaDefinition(value)) {
|
|
109
|
+
result[key] = {
|
|
110
|
+
...value,
|
|
111
|
+
fields: applySchemaDefaults(value.fields, defaults)
|
|
112
|
+
};
|
|
113
|
+
} else if (typeof value === "object" && value !== null) {
|
|
114
|
+
result[key] = applySchemaDefaults(value, defaults);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
function applyDefaultsToField(field, defaults) {
|
|
120
|
+
const needsContext = defaults.context && !field.context;
|
|
121
|
+
const needsTags = defaults.tags && defaults.tags.length > 0;
|
|
122
|
+
if (!needsContext && !needsTags) return field;
|
|
123
|
+
return Object.freeze({
|
|
124
|
+
...field,
|
|
125
|
+
context: field.context ?? defaults.context ?? void 0,
|
|
126
|
+
docs: {
|
|
127
|
+
...field.docs,
|
|
128
|
+
tags: mergeTags(defaults.tags, field.docs.tags)
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function mergeTags(schemaTags, fieldTags) {
|
|
133
|
+
if (!schemaTags || schemaTags.length === 0) return fieldTags;
|
|
134
|
+
if (!fieldTags || fieldTags.length === 0) return schemaTags;
|
|
135
|
+
const merged = [...schemaTags];
|
|
136
|
+
for (const tag of fieldTags) {
|
|
137
|
+
if (!merged.includes(tag)) {
|
|
138
|
+
merged.push(tag);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return merged;
|
|
142
|
+
}
|
|
143
|
+
function deepFreeze(obj) {
|
|
144
|
+
const propNames = Object.getOwnPropertyNames(obj);
|
|
145
|
+
for (const name of propNames) {
|
|
146
|
+
const value = obj[name];
|
|
147
|
+
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
148
|
+
deepFreeze(value);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return Object.freeze(obj);
|
|
152
|
+
}
|
|
153
|
+
function extractFields(input) {
|
|
154
|
+
return isSchemaDefinition(input) ? input.fields : input;
|
|
155
|
+
}
|
|
156
|
+
function isSubGroup(value) {
|
|
157
|
+
return typeof value === "object" && value !== null && !isFieldDefinition(value) && !isSchemaDefinition(value);
|
|
158
|
+
}
|
|
159
|
+
function deepMergeConfigs(a, b) {
|
|
160
|
+
const result = { ...a };
|
|
161
|
+
for (const key of Object.keys(b)) {
|
|
162
|
+
const aVal = result[key];
|
|
163
|
+
const bVal = b[key];
|
|
164
|
+
if (isSubGroup(aVal) && isSubGroup(bVal)) {
|
|
165
|
+
result[key] = deepMergeConfigs(aVal, bVal);
|
|
166
|
+
} else {
|
|
167
|
+
result[key] = bVal;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
function mergeGraphQLSchemas(...schemas) {
|
|
173
|
+
const merged = schemas.map(extractFields).reduce(deepMergeConfigs);
|
|
174
|
+
return defineGraphQLSchema(merged);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/presentation/graphql/field/utils.ts
|
|
178
|
+
function generateFieldId(key) {
|
|
179
|
+
return key.split(".").map(
|
|
180
|
+
(segment, index) => index === 0 ? segment : segment.charAt(0).toUpperCase() + segment.slice(1)
|
|
181
|
+
).join("");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/global/exceptions/coded-error.error.ts
|
|
185
|
+
var CodedError = class extends Error {
|
|
186
|
+
/** Machine-readable error code for programmatic handling. */
|
|
187
|
+
code;
|
|
188
|
+
/**
|
|
189
|
+
* Creates a new CodedError instance.
|
|
190
|
+
*
|
|
191
|
+
* @param options - Error configuration
|
|
192
|
+
* @param options.message - Human-readable error message
|
|
193
|
+
* @param options.code - Machine-readable error code from ErrorCodes registry or custom string
|
|
194
|
+
* @param options.cause - Optional underlying error that caused this error
|
|
195
|
+
*/
|
|
196
|
+
constructor({
|
|
197
|
+
message,
|
|
198
|
+
code,
|
|
199
|
+
cause
|
|
200
|
+
}) {
|
|
201
|
+
super(message);
|
|
202
|
+
this.name = this.constructor.name;
|
|
203
|
+
this.code = code;
|
|
204
|
+
if (cause !== void 0) {
|
|
205
|
+
Object.defineProperty(this, "cause", {
|
|
206
|
+
value: cause,
|
|
207
|
+
writable: false,
|
|
208
|
+
enumerable: false,
|
|
209
|
+
configurable: true
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Factory method to create a typed error from a caught error.
|
|
215
|
+
*
|
|
216
|
+
* Subclasses should override this to provide proper error transformation.
|
|
217
|
+
* Designed for use with {@link wrapErrorAsync} and {@link wrapError}.
|
|
218
|
+
*
|
|
219
|
+
* @param _cause - The original caught error
|
|
220
|
+
* @returns A new CodedError instance
|
|
221
|
+
* @throws {Error} If not overridden by subclass
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```typescript
|
|
225
|
+
* class NotFoundError extends UseCaseError {
|
|
226
|
+
* static override fromError(cause: unknown): NotFoundError {
|
|
227
|
+
* return new NotFoundError({
|
|
228
|
+
* message: 'Resource not found',
|
|
229
|
+
* cause,
|
|
230
|
+
* });
|
|
231
|
+
* }
|
|
232
|
+
* }
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
static fromError(_cause) {
|
|
236
|
+
throw new Error(`${this.name}.fromError() must be implemented by subclass`);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// src/global/exceptions/error-codes.const.ts
|
|
241
|
+
var ErrorCodes = {
|
|
242
|
+
/**
|
|
243
|
+
* Domain layer error codes.
|
|
244
|
+
* Used for business rule violations and invariant failures.
|
|
245
|
+
*/
|
|
246
|
+
Domain: {
|
|
247
|
+
/** Generic domain error */
|
|
248
|
+
DOMAIN_ERROR: "DOMAIN_ERROR",
|
|
249
|
+
/** Business invariant was violated */
|
|
250
|
+
INVARIANT_VIOLATION: "INVARIANT_VIOLATION",
|
|
251
|
+
/** Aggregate was partially loaded (missing required relations) */
|
|
252
|
+
PARTIAL_LOAD: "PARTIAL_LOAD"
|
|
253
|
+
},
|
|
254
|
+
/**
|
|
255
|
+
* Application layer (use case) error codes.
|
|
256
|
+
* Used for orchestration failures and business operation errors.
|
|
257
|
+
*/
|
|
258
|
+
App: {
|
|
259
|
+
/** Generic use case error */
|
|
260
|
+
USE_CASE_ERROR: "USE_CASE_ERROR",
|
|
261
|
+
/** Requested resource was not found */
|
|
262
|
+
NOT_FOUND: "NOT_FOUND",
|
|
263
|
+
/** Resource state conflict (e.g., duplicate, already exists) */
|
|
264
|
+
CONFLICT: "CONFLICT",
|
|
265
|
+
/** Request is valid but cannot be processed due to business rules */
|
|
266
|
+
UNPROCESSABLE: "UNPROCESSABLE",
|
|
267
|
+
/** Authorization denied - user lacks permission for this operation */
|
|
268
|
+
FORBIDDEN: "FORBIDDEN",
|
|
269
|
+
/** Authentication required or invalid - user is not authenticated */
|
|
270
|
+
UNAUTHORIZED: "UNAUTHORIZED"
|
|
271
|
+
},
|
|
272
|
+
/**
|
|
273
|
+
* Infrastructure layer error codes.
|
|
274
|
+
* Used for data access, external services, and I/O failures.
|
|
275
|
+
*/
|
|
276
|
+
Infra: {
|
|
277
|
+
/** Generic infrastructure error */
|
|
278
|
+
INFRA_ERROR: "INFRA_ERROR",
|
|
279
|
+
/** Database operation failed */
|
|
280
|
+
DB_ERROR: "DB_ERROR",
|
|
281
|
+
/** Network connectivity or communication error */
|
|
282
|
+
NETWORK_ERROR: "NETWORK_ERROR",
|
|
283
|
+
/** Operation timed out */
|
|
284
|
+
TIMEOUT_ERROR: "TIMEOUT_ERROR",
|
|
285
|
+
/** External/third-party service error */
|
|
286
|
+
EXTERNAL_SERVICE_ERROR: "EXTERNAL_SERVICE_ERROR"
|
|
287
|
+
},
|
|
288
|
+
/**
|
|
289
|
+
* Presentation layer error codes.
|
|
290
|
+
* Used for controller, request handling, and authorization errors.
|
|
291
|
+
*/
|
|
292
|
+
Presentation: {
|
|
293
|
+
/** Generic controller error */
|
|
294
|
+
CONTROLLER_ERROR: "CONTROLLER_ERROR",
|
|
295
|
+
/** Request denied due to authorization failure */
|
|
296
|
+
ACCESS_DENIED: "ACCESS_DENIED",
|
|
297
|
+
/** Request validation failed (malformed input) */
|
|
298
|
+
INVALID_REQUEST: "INVALID_REQUEST"
|
|
299
|
+
},
|
|
300
|
+
/**
|
|
301
|
+
* Global/cross-cutting error codes.
|
|
302
|
+
* Used for validation and other cross-layer concerns.
|
|
303
|
+
*/
|
|
304
|
+
Global: {
|
|
305
|
+
/** Object/schema validation failed */
|
|
306
|
+
OBJECT_VALIDATION_ERROR: "OBJECT_VALIDATION_ERROR"
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
// src/global/exceptions/object-validation.error.ts
|
|
311
|
+
var ObjectValidationError = class _ObjectValidationError extends CodedError {
|
|
312
|
+
/**
|
|
313
|
+
* Array of field-level validation errors.
|
|
314
|
+
*
|
|
315
|
+
* Each entry contains:
|
|
316
|
+
* - `field`: Dot-notation path to the invalid field (e.g., 'user.email')
|
|
317
|
+
* - `message`: Human-readable validation failure message
|
|
318
|
+
*/
|
|
319
|
+
validationErrors;
|
|
320
|
+
/**
|
|
321
|
+
* Creates a new ObjectValidationError instance.
|
|
322
|
+
*
|
|
323
|
+
* @param options - Error configuration
|
|
324
|
+
* @param options.message - Human-readable summary message
|
|
325
|
+
* @param options.code - Machine-readable error code (default: 'OBJECT_VALIDATION_ERROR')
|
|
326
|
+
* @param options.cause - Optional underlying error from validation library
|
|
327
|
+
* @param options.validationErrors - Array of field-level validation errors
|
|
328
|
+
*/
|
|
329
|
+
constructor({
|
|
330
|
+
message,
|
|
331
|
+
code = ErrorCodes.Global.OBJECT_VALIDATION_ERROR,
|
|
332
|
+
cause,
|
|
333
|
+
validationErrors
|
|
334
|
+
}) {
|
|
335
|
+
super({ message, code, cause });
|
|
336
|
+
this.validationErrors = validationErrors;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Creates an ObjectValidationError from a caught error.
|
|
340
|
+
*
|
|
341
|
+
* @param cause - The original caught error
|
|
342
|
+
* @returns A new ObjectValidationError instance with the cause attached
|
|
343
|
+
*/
|
|
344
|
+
static fromError(cause) {
|
|
345
|
+
return new _ObjectValidationError({
|
|
346
|
+
message: cause instanceof Error ? cause.message : "Validation failed",
|
|
347
|
+
cause,
|
|
348
|
+
validationErrors: []
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// src/domain/exceptions/domain.error.ts
|
|
354
|
+
var DomainError = class _DomainError extends CodedError {
|
|
355
|
+
/**
|
|
356
|
+
* Creates a new DomainError instance.
|
|
357
|
+
*
|
|
358
|
+
* @param options - Error configuration
|
|
359
|
+
* @param options.message - Human-readable error description
|
|
360
|
+
* @param options.code - Machine-readable error code (default: 'DOMAIN_ERROR')
|
|
361
|
+
* @param options.cause - Optional underlying error
|
|
362
|
+
*/
|
|
363
|
+
constructor({
|
|
364
|
+
message,
|
|
365
|
+
code = ErrorCodes.Domain.DOMAIN_ERROR,
|
|
366
|
+
cause
|
|
367
|
+
}) {
|
|
368
|
+
super({ message, code, cause });
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Creates a DomainError from a caught error.
|
|
372
|
+
*
|
|
373
|
+
* @param cause - The original caught error
|
|
374
|
+
* @returns A new DomainError instance with the cause attached
|
|
375
|
+
*/
|
|
376
|
+
static fromError(cause) {
|
|
377
|
+
return new _DomainError({
|
|
378
|
+
message: cause instanceof Error ? cause.message : "Domain error",
|
|
379
|
+
cause
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
// src/app/exceptions/use-case.error.ts
|
|
385
|
+
var UseCaseError = class _UseCaseError extends CodedError {
|
|
386
|
+
/**
|
|
387
|
+
* Creates a new UseCaseError instance.
|
|
388
|
+
*
|
|
389
|
+
* @param options - Error configuration
|
|
390
|
+
* @param options.message - Human-readable error description
|
|
391
|
+
* @param options.code - Machine-readable error code (default: 'USE_CASE_ERROR')
|
|
392
|
+
* @param options.cause - Optional underlying error
|
|
393
|
+
*/
|
|
394
|
+
constructor({
|
|
395
|
+
message,
|
|
396
|
+
code = ErrorCodes.App.USE_CASE_ERROR,
|
|
397
|
+
cause
|
|
398
|
+
}) {
|
|
399
|
+
super({ message, code, cause });
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Creates a UseCaseError from a caught error.
|
|
403
|
+
*
|
|
404
|
+
* @param cause - The original caught error
|
|
405
|
+
* @returns A new UseCaseError instance with the cause attached
|
|
406
|
+
*/
|
|
407
|
+
static fromError(cause) {
|
|
408
|
+
return new _UseCaseError({
|
|
409
|
+
message: cause instanceof Error ? cause.message : "Use case error",
|
|
410
|
+
cause
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
// src/app/exceptions/not-found.error.ts
|
|
416
|
+
var NotFoundError = class _NotFoundError extends UseCaseError {
|
|
417
|
+
/**
|
|
418
|
+
* Creates a new NotFoundError instance.
|
|
419
|
+
*
|
|
420
|
+
* @param options - Error configuration
|
|
421
|
+
* @param options.message - Description of what was not found
|
|
422
|
+
* @param options.code - Machine-readable error code (default: 'NOT_FOUND')
|
|
423
|
+
* @param options.cause - Optional underlying error
|
|
424
|
+
*/
|
|
425
|
+
constructor({
|
|
426
|
+
message,
|
|
427
|
+
code = ErrorCodes.App.NOT_FOUND,
|
|
428
|
+
cause
|
|
429
|
+
}) {
|
|
430
|
+
super({ message, code, cause });
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Creates a NotFoundError from a caught error.
|
|
434
|
+
*
|
|
435
|
+
* @param cause - The original caught error
|
|
436
|
+
* @returns A new NotFoundError instance with the cause attached
|
|
437
|
+
*/
|
|
438
|
+
static fromError(cause) {
|
|
439
|
+
return new _NotFoundError({
|
|
440
|
+
message: cause instanceof Error ? cause.message : "Resource not found",
|
|
441
|
+
cause
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
// src/app/exceptions/conflict.error.ts
|
|
447
|
+
var ConflictError = class _ConflictError extends UseCaseError {
|
|
448
|
+
/**
|
|
449
|
+
* Creates a new ConflictError instance.
|
|
450
|
+
*
|
|
451
|
+
* @param options - Error configuration
|
|
452
|
+
* @param options.message - Description of the conflict
|
|
453
|
+
* @param options.code - Machine-readable error code (default: 'CONFLICT')
|
|
454
|
+
* @param options.cause - Optional underlying error
|
|
455
|
+
*/
|
|
456
|
+
constructor({
|
|
457
|
+
message,
|
|
458
|
+
code = ErrorCodes.App.CONFLICT,
|
|
459
|
+
cause
|
|
460
|
+
}) {
|
|
461
|
+
super({ message, code, cause });
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Creates a ConflictError from a caught error.
|
|
465
|
+
*
|
|
466
|
+
* @param cause - The original caught error
|
|
467
|
+
* @returns A new ConflictError instance with the cause attached
|
|
468
|
+
*/
|
|
469
|
+
static fromError(cause) {
|
|
470
|
+
return new _ConflictError({
|
|
471
|
+
message: cause instanceof Error ? cause.message : "Conflict error",
|
|
472
|
+
cause
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// src/app/exceptions/unprocessable.error.ts
|
|
478
|
+
var UnprocessableError = class _UnprocessableError extends UseCaseError {
|
|
479
|
+
/**
|
|
480
|
+
* Creates a new UnprocessableError instance.
|
|
481
|
+
*
|
|
482
|
+
* @param options - Error configuration
|
|
483
|
+
* @param options.message - Description of why the request cannot be processed
|
|
484
|
+
* @param options.code - Machine-readable error code (default: 'UNPROCESSABLE')
|
|
485
|
+
* @param options.cause - Optional underlying error
|
|
486
|
+
*/
|
|
487
|
+
constructor({
|
|
488
|
+
message,
|
|
489
|
+
code = ErrorCodes.App.UNPROCESSABLE,
|
|
490
|
+
cause
|
|
491
|
+
}) {
|
|
492
|
+
super({ message, code, cause });
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Creates an UnprocessableError from a caught error.
|
|
496
|
+
*
|
|
497
|
+
* @param cause - The original caught error
|
|
498
|
+
* @returns A new UnprocessableError instance with the cause attached
|
|
499
|
+
*/
|
|
500
|
+
static fromError(cause) {
|
|
501
|
+
return new _UnprocessableError({
|
|
502
|
+
message: cause instanceof Error ? cause.message : "Unprocessable request",
|
|
503
|
+
cause
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
// src/app/exceptions/forbidden.error.ts
|
|
509
|
+
var ForbiddenError = class _ForbiddenError extends UseCaseError {
|
|
510
|
+
/**
|
|
511
|
+
* Creates a new ForbiddenError instance.
|
|
512
|
+
*
|
|
513
|
+
* @param options - Error configuration
|
|
514
|
+
* @param options.message - Description of why authorization was denied
|
|
515
|
+
* @param options.code - Machine-readable error code (default: 'FORBIDDEN')
|
|
516
|
+
* @param options.cause - Optional underlying error
|
|
517
|
+
*/
|
|
518
|
+
constructor({
|
|
519
|
+
message,
|
|
520
|
+
code = ErrorCodes.App.FORBIDDEN,
|
|
521
|
+
cause
|
|
522
|
+
}) {
|
|
523
|
+
super({ message, code, cause });
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Creates a ForbiddenError from a caught error.
|
|
527
|
+
*
|
|
528
|
+
* @param cause - The original caught error
|
|
529
|
+
* @returns A new ForbiddenError instance with the cause attached
|
|
530
|
+
*/
|
|
531
|
+
static fromError(cause) {
|
|
532
|
+
return new _ForbiddenError({
|
|
533
|
+
message: cause instanceof Error ? cause.message : "Operation forbidden",
|
|
534
|
+
cause
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
// src/app/exceptions/unauthorized.error.ts
|
|
540
|
+
var UnauthorizedError = class _UnauthorizedError extends UseCaseError {
|
|
541
|
+
/**
|
|
542
|
+
* Creates a new UnauthorizedError instance.
|
|
543
|
+
*
|
|
544
|
+
* @param options - Error configuration
|
|
545
|
+
* @param options.message - Description of why authentication failed
|
|
546
|
+
* @param options.code - Machine-readable error code (default: 'UNAUTHORIZED')
|
|
547
|
+
* @param options.cause - Optional underlying error
|
|
548
|
+
*/
|
|
549
|
+
constructor({
|
|
550
|
+
message,
|
|
551
|
+
code = ErrorCodes.App.UNAUTHORIZED,
|
|
552
|
+
cause
|
|
553
|
+
}) {
|
|
554
|
+
super({ message, code, cause });
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Creates an UnauthorizedError from a caught error.
|
|
558
|
+
*
|
|
559
|
+
* @param cause - The original caught error
|
|
560
|
+
* @returns A new UnauthorizedError instance with the cause attached
|
|
561
|
+
*/
|
|
562
|
+
static fromError(cause) {
|
|
563
|
+
return new _UnauthorizedError({
|
|
564
|
+
message: cause instanceof Error ? cause.message : "Authentication required",
|
|
565
|
+
cause
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
// src/infra/exceptions/infra.error.ts
|
|
571
|
+
var InfraError = class _InfraError extends CodedError {
|
|
572
|
+
/**
|
|
573
|
+
* Creates a new InfraError instance.
|
|
574
|
+
*
|
|
575
|
+
* @param options - Error configuration
|
|
576
|
+
* @param options.message - Human-readable error description
|
|
577
|
+
* @param options.code - Machine-readable error code (default: 'INFRA_ERROR')
|
|
578
|
+
* @param options.cause - Optional underlying error
|
|
579
|
+
*/
|
|
580
|
+
constructor({
|
|
581
|
+
message,
|
|
582
|
+
code = ErrorCodes.Infra.INFRA_ERROR,
|
|
583
|
+
cause
|
|
584
|
+
}) {
|
|
585
|
+
super({ message, code, cause });
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Creates an InfraError from a caught error.
|
|
589
|
+
*
|
|
590
|
+
* @param cause - The original caught error
|
|
591
|
+
* @returns A new InfraError instance with the cause attached
|
|
592
|
+
*/
|
|
593
|
+
static fromError(cause) {
|
|
594
|
+
return new _InfraError({
|
|
595
|
+
message: cause instanceof Error ? cause.message : "Infrastructure error",
|
|
596
|
+
cause
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
// src/presentation/exceptions/access-denied.error.ts
|
|
602
|
+
var AccessDeniedError = class _AccessDeniedError extends CodedError {
|
|
603
|
+
/**
|
|
604
|
+
* Creates a new AccessDeniedError instance.
|
|
605
|
+
*
|
|
606
|
+
* @param options - Error configuration
|
|
607
|
+
* @param options.message - Description of why access was denied
|
|
608
|
+
* @param options.code - Machine-readable error code (default: 'ACCESS_DENIED')
|
|
609
|
+
* @param options.cause - Optional underlying error
|
|
610
|
+
*/
|
|
611
|
+
constructor({
|
|
612
|
+
message,
|
|
613
|
+
code = ErrorCodes.Presentation.ACCESS_DENIED,
|
|
614
|
+
cause
|
|
615
|
+
}) {
|
|
616
|
+
super({ message, code, cause });
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Creates an AccessDeniedError from a caught error.
|
|
620
|
+
*
|
|
621
|
+
* @param cause - The original caught error
|
|
622
|
+
* @returns A new AccessDeniedError instance with the cause attached
|
|
623
|
+
*/
|
|
624
|
+
static fromError(cause) {
|
|
625
|
+
return new _AccessDeniedError({
|
|
626
|
+
message: cause instanceof Error ? cause.message : "Access denied",
|
|
627
|
+
cause
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
// src/presentation/http/shared/error-mapping.ts
|
|
633
|
+
function isErrorType(error, typeName) {
|
|
634
|
+
if (!error || typeof error !== "object") return false;
|
|
635
|
+
const constructor = Object.prototype.hasOwnProperty.call(error, "constructor") ? error.constructor : Object.getPrototypeOf(error)?.constructor;
|
|
636
|
+
if (!constructor) return false;
|
|
637
|
+
const name = constructor.name;
|
|
638
|
+
return name === typeName || name === `_${typeName}`;
|
|
639
|
+
}
|
|
640
|
+
function hasValidationErrors(error) {
|
|
641
|
+
if (!error || typeof error !== "object") return false;
|
|
642
|
+
return "validationErrors" in error && Array.isArray(error.validationErrors);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// src/presentation/graphql/shared/error-mapping.ts
|
|
646
|
+
var MASKED_ERROR = {
|
|
647
|
+
message: "An unexpected error occurred",
|
|
648
|
+
extensions: { code: "INTERNAL_ERROR" }
|
|
649
|
+
};
|
|
650
|
+
var INTERNAL_ERROR_TYPES = [
|
|
651
|
+
"DomainError",
|
|
652
|
+
"InfraError",
|
|
653
|
+
"ControllerError",
|
|
654
|
+
"NetworkError",
|
|
655
|
+
"PersistenceError",
|
|
656
|
+
"ExternalServiceError",
|
|
657
|
+
"InvariantViolationError"
|
|
658
|
+
];
|
|
659
|
+
function getGraphQLErrorCode(error) {
|
|
660
|
+
if (error instanceof ObjectValidationError) return "VALIDATION_ERROR";
|
|
661
|
+
if (error instanceof UnauthorizedError) return "FORBIDDEN";
|
|
662
|
+
if (error instanceof ForbiddenError) return "FORBIDDEN";
|
|
663
|
+
if (error instanceof AccessDeniedError) return "FORBIDDEN";
|
|
664
|
+
if (error instanceof NotFoundError) return "NOT_FOUND";
|
|
665
|
+
if (error instanceof ConflictError) return "CONFLICT";
|
|
666
|
+
if (error instanceof UnprocessableError) return "UNPROCESSABLE";
|
|
667
|
+
if (error instanceof UseCaseError) return "BAD_REQUEST";
|
|
668
|
+
if (isErrorType(error, "ObjectValidationError")) return "VALIDATION_ERROR";
|
|
669
|
+
if (isErrorType(error, "UnauthorizedError")) return "FORBIDDEN";
|
|
670
|
+
if (isErrorType(error, "ForbiddenError")) return "FORBIDDEN";
|
|
671
|
+
if (isErrorType(error, "AccessDeniedError")) return "FORBIDDEN";
|
|
672
|
+
if (isErrorType(error, "NotFoundError")) return "NOT_FOUND";
|
|
673
|
+
if (isErrorType(error, "ConflictError")) return "CONFLICT";
|
|
674
|
+
if (isErrorType(error, "UnprocessableError")) return "UNPROCESSABLE";
|
|
675
|
+
if (isErrorType(error, "UseCaseError")) return "BAD_REQUEST";
|
|
676
|
+
return "INTERNAL_ERROR";
|
|
677
|
+
}
|
|
678
|
+
function shouldMaskGraphQLError(error) {
|
|
679
|
+
if (error instanceof DomainError || error instanceof InfraError) {
|
|
680
|
+
return true;
|
|
681
|
+
}
|
|
682
|
+
for (const errorType of INTERNAL_ERROR_TYPES) {
|
|
683
|
+
if (isErrorType(error, errorType)) {
|
|
684
|
+
return true;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
return false;
|
|
688
|
+
}
|
|
689
|
+
function mapErrorToGraphQLError(error) {
|
|
690
|
+
if (shouldMaskGraphQLError(error)) {
|
|
691
|
+
return MASKED_ERROR;
|
|
692
|
+
}
|
|
693
|
+
const code = getGraphQLErrorCode(error);
|
|
694
|
+
if (error instanceof ObjectValidationError) {
|
|
695
|
+
return buildValidationError(error.message, code, error.code, error.validationErrors);
|
|
696
|
+
}
|
|
697
|
+
if (isErrorType(error, "ObjectValidationError") && hasValidationErrors(error)) {
|
|
698
|
+
return buildValidationError(error.message, code, error.code, error.validationErrors);
|
|
699
|
+
}
|
|
700
|
+
if (error instanceof CodedError) {
|
|
701
|
+
return {
|
|
702
|
+
message: error.message,
|
|
703
|
+
extensions: { code, originalCode: error.code }
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
if (isErrorType(error, "CodedError")) {
|
|
707
|
+
return {
|
|
708
|
+
message: error.message,
|
|
709
|
+
extensions: { code, originalCode: error.code }
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
if (error && typeof error === "object" && "message" in error && "code" in error && typeof error.message === "string" && typeof error.code === "string") {
|
|
713
|
+
return {
|
|
714
|
+
message: error.message,
|
|
715
|
+
extensions: { code, originalCode: error.code }
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
return MASKED_ERROR;
|
|
719
|
+
}
|
|
720
|
+
function buildValidationError(message, code, originalCode, validationErrors) {
|
|
721
|
+
return {
|
|
722
|
+
message,
|
|
723
|
+
extensions: {
|
|
724
|
+
code,
|
|
725
|
+
originalCode,
|
|
726
|
+
validationErrors: validationErrors.map(
|
|
727
|
+
(e) => ({
|
|
728
|
+
field: e.field,
|
|
729
|
+
message: e.message
|
|
730
|
+
})
|
|
731
|
+
)
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// src/presentation/graphql/server/types.ts
|
|
737
|
+
function isSimpleGraphQLHandlerConfig(config) {
|
|
738
|
+
return "handler" in config && typeof config.handler === "function";
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// src/presentation/graphql/server/create-graphql-routes.ts
|
|
742
|
+
function createGraphQLRoutesInternal(schema, handlers, options) {
|
|
743
|
+
const config = isSchemaDefinition(schema) ? schema.fields : schema;
|
|
744
|
+
const collectedFields = collectFields(config);
|
|
745
|
+
const result = [];
|
|
746
|
+
const resolvedOptions = {
|
|
747
|
+
...options,
|
|
748
|
+
validateInput: options?.validateInput ?? true,
|
|
749
|
+
validateOutput: options?.validateOutput ?? true,
|
|
750
|
+
allowPartial: options?.allowPartial ?? false
|
|
751
|
+
};
|
|
752
|
+
for (const { key, field } of collectedFields) {
|
|
753
|
+
const handlerConfig = handlers[key];
|
|
754
|
+
if (!handlerConfig) {
|
|
755
|
+
if (resolvedOptions.allowPartial) {
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
throw new Error(
|
|
759
|
+
`Missing handler for field "${key}". All fields must have a handler configuration.`
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
result.push(createFieldHandler(key, field, handlerConfig, resolvedOptions));
|
|
763
|
+
}
|
|
764
|
+
return result;
|
|
765
|
+
}
|
|
766
|
+
function createFieldHandler(key, field, config, options) {
|
|
767
|
+
const middleware = config.middleware ?? [];
|
|
768
|
+
const globalMiddleware = options?.middleware ?? [];
|
|
769
|
+
const allMiddleware = [...globalMiddleware, ...middleware];
|
|
770
|
+
const shouldValidateInput = options.validateInput ?? true;
|
|
771
|
+
const shouldValidateOutput = options.validateOutput ?? true;
|
|
772
|
+
return {
|
|
773
|
+
key,
|
|
774
|
+
operation: field.operation,
|
|
775
|
+
metadata: {
|
|
776
|
+
fieldId: generateFieldId(key),
|
|
777
|
+
description: field.docs.description,
|
|
778
|
+
tags: field.docs.tags,
|
|
779
|
+
deprecated: field.docs.deprecated,
|
|
780
|
+
deprecationReason: field.docs.deprecationReason
|
|
781
|
+
},
|
|
782
|
+
handler: async (rawArgs, rawContext) => {
|
|
783
|
+
const context = options?.createContext ? options.createContext(rawContext) : rawContext ?? { requestId: generateRequestId() };
|
|
784
|
+
let validatedContext = context;
|
|
785
|
+
if (field.context) {
|
|
786
|
+
const contextResult = validateContextData(field, context);
|
|
787
|
+
if (!contextResult.success) {
|
|
788
|
+
throw new UnauthorizedError({
|
|
789
|
+
message: "Authentication required"
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
validatedContext = contextResult.data;
|
|
793
|
+
}
|
|
794
|
+
let validatedInput = rawArgs;
|
|
795
|
+
if (shouldValidateInput && field.input) {
|
|
796
|
+
const inputResult = validateInputData(field, rawArgs);
|
|
797
|
+
if (!inputResult.success) {
|
|
798
|
+
const errors = inputResult.errors ?? [];
|
|
799
|
+
throw new ObjectValidationError({
|
|
800
|
+
message: "Input validation failed",
|
|
801
|
+
validationErrors: errors.map((e) => ({
|
|
802
|
+
field: e.path.join("."),
|
|
803
|
+
message: e.message
|
|
804
|
+
}))
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
validatedInput = inputResult.data;
|
|
808
|
+
}
|
|
809
|
+
const validatedArgs = {
|
|
810
|
+
input: validatedInput,
|
|
811
|
+
raw: rawArgs
|
|
812
|
+
};
|
|
813
|
+
const executePipeline = async () => {
|
|
814
|
+
if (isSimpleGraphQLHandlerConfig(config)) {
|
|
815
|
+
return config.handler(
|
|
816
|
+
validatedArgs,
|
|
817
|
+
validatedContext
|
|
818
|
+
);
|
|
819
|
+
} else {
|
|
820
|
+
const { argsMapper, useCase, responseMapper } = config;
|
|
821
|
+
const input = argsMapper(
|
|
822
|
+
validatedArgs,
|
|
823
|
+
validatedContext
|
|
824
|
+
);
|
|
825
|
+
const output = await useCase.execute(input);
|
|
826
|
+
return responseMapper(output);
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
let result;
|
|
830
|
+
if (allMiddleware.length === 0) {
|
|
831
|
+
result = await executePipeline();
|
|
832
|
+
} else {
|
|
833
|
+
let index = 0;
|
|
834
|
+
let called = false;
|
|
835
|
+
const next = async () => {
|
|
836
|
+
if (index >= allMiddleware.length) {
|
|
837
|
+
if (called) throw new Error("next() called after pipeline already executed");
|
|
838
|
+
called = true;
|
|
839
|
+
return executePipeline();
|
|
840
|
+
}
|
|
841
|
+
const currentIndex = index++;
|
|
842
|
+
const mw = allMiddleware[currentIndex];
|
|
843
|
+
return mw(rawArgs, context, next);
|
|
844
|
+
};
|
|
845
|
+
result = await next();
|
|
846
|
+
}
|
|
847
|
+
if (shouldValidateOutput && field.output) {
|
|
848
|
+
const outputResult = validateOutputData(field, result);
|
|
849
|
+
if (!outputResult.success) {
|
|
850
|
+
const details = outputResult.errors?.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ");
|
|
851
|
+
throw new ObjectValidationError({
|
|
852
|
+
message: `Output validation failed for field "${key}": ${details}`,
|
|
853
|
+
validationErrors: (outputResult.errors ?? []).map((e) => ({
|
|
854
|
+
field: e.path.join("."),
|
|
855
|
+
message: e.message
|
|
856
|
+
}))
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return result;
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
function validateInputData(field, args) {
|
|
865
|
+
const schema = field.input;
|
|
866
|
+
if (!schema) return { success: true, data: args };
|
|
867
|
+
const result = schema.validate(args);
|
|
868
|
+
if (result.success) {
|
|
869
|
+
return { success: true, data: result.data };
|
|
870
|
+
}
|
|
871
|
+
const errors = result.issues.map((issue) => ({
|
|
872
|
+
...issue,
|
|
873
|
+
path: ["input", ...issue.path]
|
|
874
|
+
}));
|
|
875
|
+
return { success: false, errors };
|
|
876
|
+
}
|
|
877
|
+
function validateOutputData(field, data) {
|
|
878
|
+
const schema = field.output;
|
|
879
|
+
if (!schema) return { success: true, data };
|
|
880
|
+
const result = schema.validate(data);
|
|
881
|
+
if (result.success) {
|
|
882
|
+
return { success: true, data: result.data };
|
|
883
|
+
}
|
|
884
|
+
const errors = result.issues.map((issue) => ({
|
|
885
|
+
...issue,
|
|
886
|
+
path: ["output", ...issue.path]
|
|
887
|
+
}));
|
|
888
|
+
return { success: false, errors };
|
|
889
|
+
}
|
|
890
|
+
function validateContextData(field, context) {
|
|
891
|
+
const schema = field.context;
|
|
892
|
+
if (!schema) return { success: true, data: context };
|
|
893
|
+
const result = schema.validate(context);
|
|
894
|
+
if (result.success) {
|
|
895
|
+
return { success: true, data: result.data };
|
|
896
|
+
}
|
|
897
|
+
const errors = result.issues.map((issue) => ({
|
|
898
|
+
...issue,
|
|
899
|
+
path: ["context", ...issue.path]
|
|
900
|
+
}));
|
|
901
|
+
return { success: false, errors };
|
|
902
|
+
}
|
|
903
|
+
function generateRequestId() {
|
|
904
|
+
return `gql_${crypto.randomUUID()}`;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// src/presentation/graphql/server/graphql-routes-builder.ts
|
|
908
|
+
var GraphQLRoutesBuilderImpl = class _GraphQLRoutesBuilderImpl {
|
|
909
|
+
schema;
|
|
910
|
+
handlers;
|
|
911
|
+
constructor(schema, handlers) {
|
|
912
|
+
this.schema = schema;
|
|
913
|
+
this.handlers = handlers ?? /* @__PURE__ */ new Map();
|
|
914
|
+
}
|
|
915
|
+
handle(key, handlerOrConfig) {
|
|
916
|
+
const config = typeof handlerOrConfig === "function" ? { handler: handlerOrConfig } : handlerOrConfig;
|
|
917
|
+
const newHandlers = new Map(this.handlers);
|
|
918
|
+
newHandlers.set(key, config);
|
|
919
|
+
return new _GraphQLRoutesBuilderImpl(
|
|
920
|
+
this.schema,
|
|
921
|
+
newHandlers
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
handleWithUseCase(key, config) {
|
|
925
|
+
const newHandlers = new Map(this.handlers);
|
|
926
|
+
newHandlers.set(
|
|
927
|
+
key,
|
|
928
|
+
config
|
|
929
|
+
);
|
|
930
|
+
return new _GraphQLRoutesBuilderImpl(
|
|
931
|
+
this.schema,
|
|
932
|
+
newHandlers
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
build(options) {
|
|
936
|
+
return createGraphQLRoutesInternal(this.schema, Object.fromEntries(this.handlers), options);
|
|
937
|
+
}
|
|
938
|
+
buildPartial(options) {
|
|
939
|
+
return createGraphQLRoutesInternal(this.schema, Object.fromEntries(this.handlers), {
|
|
940
|
+
...options,
|
|
941
|
+
allowPartial: true
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
function graphqlRoutes(schema) {
|
|
946
|
+
return new GraphQLRoutesBuilderImpl(schema);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// src/presentation/graphql/sdl/generate.ts
|
|
950
|
+
function generateGraphQLSDL(schema, config) {
|
|
951
|
+
const fields = isSchemaDefinition(schema) ? schema.fields : schema;
|
|
952
|
+
const collectedFields = collectFields(fields);
|
|
953
|
+
const includeDescriptions = config?.includeDescriptions ?? true;
|
|
954
|
+
const includeDeprecations = config?.includeDeprecations ?? true;
|
|
955
|
+
const queries = [];
|
|
956
|
+
const mutations = [];
|
|
957
|
+
const subscriptions = [];
|
|
958
|
+
for (const { key, field } of collectedFields) {
|
|
959
|
+
const fieldId = generateFieldId(key);
|
|
960
|
+
if (field.operation === "query") {
|
|
961
|
+
queries.push({ fieldId, field });
|
|
962
|
+
} else if (field.operation === "mutation") {
|
|
963
|
+
mutations.push({ fieldId, field });
|
|
964
|
+
} else if (field.operation === "subscription") {
|
|
965
|
+
subscriptions.push({ fieldId, field });
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
const namedTypes = /* @__PURE__ */ new Map();
|
|
969
|
+
const lines = [];
|
|
970
|
+
if (config?.preamble) {
|
|
971
|
+
lines.push(config.preamble);
|
|
972
|
+
lines.push("");
|
|
973
|
+
}
|
|
974
|
+
if (queries.length > 0) {
|
|
975
|
+
lines.push("type Query {");
|
|
976
|
+
for (const { fieldId, field } of queries) {
|
|
977
|
+
const fieldLine = buildFieldLine(
|
|
978
|
+
fieldId,
|
|
979
|
+
field,
|
|
980
|
+
namedTypes,
|
|
981
|
+
includeDescriptions,
|
|
982
|
+
includeDeprecations
|
|
983
|
+
);
|
|
984
|
+
lines.push(fieldLine);
|
|
985
|
+
}
|
|
986
|
+
lines.push("}");
|
|
987
|
+
lines.push("");
|
|
988
|
+
}
|
|
989
|
+
if (mutations.length > 0) {
|
|
990
|
+
lines.push("type Mutation {");
|
|
991
|
+
for (const { fieldId, field } of mutations) {
|
|
992
|
+
const fieldLine = buildFieldLine(
|
|
993
|
+
fieldId,
|
|
994
|
+
field,
|
|
995
|
+
namedTypes,
|
|
996
|
+
includeDescriptions,
|
|
997
|
+
includeDeprecations
|
|
998
|
+
);
|
|
999
|
+
lines.push(fieldLine);
|
|
1000
|
+
}
|
|
1001
|
+
lines.push("}");
|
|
1002
|
+
lines.push("");
|
|
1003
|
+
}
|
|
1004
|
+
if (subscriptions.length > 0) {
|
|
1005
|
+
lines.push("type Subscription {");
|
|
1006
|
+
for (const { fieldId, field } of subscriptions) {
|
|
1007
|
+
const fieldLine = buildFieldLine(
|
|
1008
|
+
fieldId,
|
|
1009
|
+
field,
|
|
1010
|
+
namedTypes,
|
|
1011
|
+
includeDescriptions,
|
|
1012
|
+
includeDeprecations
|
|
1013
|
+
);
|
|
1014
|
+
lines.push(fieldLine);
|
|
1015
|
+
}
|
|
1016
|
+
lines.push("}");
|
|
1017
|
+
lines.push("");
|
|
1018
|
+
}
|
|
1019
|
+
for (const [, typeBody] of namedTypes) {
|
|
1020
|
+
lines.push(typeBody);
|
|
1021
|
+
lines.push("");
|
|
1022
|
+
}
|
|
1023
|
+
return lines.join("\n").trimEnd() + "\n";
|
|
1024
|
+
}
|
|
1025
|
+
function buildFieldLine(fieldId, field, namedTypes, includeDescriptions, includeDeprecations) {
|
|
1026
|
+
const parts = [];
|
|
1027
|
+
if (includeDescriptions && field.docs.description) {
|
|
1028
|
+
const escaped = field.docs.description.replace(/"""/g, '\\"""');
|
|
1029
|
+
parts.push(` """${escaped}"""`);
|
|
1030
|
+
}
|
|
1031
|
+
const inputTypeName = field.input ? `${capitalize(fieldId)}Input` : void 0;
|
|
1032
|
+
const outputTypeName = field.output ? `${capitalize(fieldId)}Output` : void 0;
|
|
1033
|
+
if (field.input && inputTypeName) {
|
|
1034
|
+
const jsonSchema = field.input.toJsonSchema();
|
|
1035
|
+
namedTypes.set(inputTypeName, buildInputType(inputTypeName, jsonSchema));
|
|
1036
|
+
}
|
|
1037
|
+
if (field.output && outputTypeName) {
|
|
1038
|
+
const jsonSchema = field.output.toJsonSchema();
|
|
1039
|
+
namedTypes.set(outputTypeName, buildOutputType(outputTypeName, jsonSchema));
|
|
1040
|
+
}
|
|
1041
|
+
let signature = ` ${fieldId}`;
|
|
1042
|
+
if (inputTypeName) {
|
|
1043
|
+
signature += `(input: ${inputTypeName}!)`;
|
|
1044
|
+
}
|
|
1045
|
+
signature += ": ";
|
|
1046
|
+
signature += outputTypeName ?? "JSON";
|
|
1047
|
+
if (includeDeprecations && field.docs.deprecated) {
|
|
1048
|
+
const reason = field.docs.deprecationReason;
|
|
1049
|
+
signature += reason ? ` @deprecated(reason: "${escapeSDLString(reason)}")` : " @deprecated";
|
|
1050
|
+
}
|
|
1051
|
+
if (parts.length > 0) {
|
|
1052
|
+
return parts.join("\n") + "\n" + signature;
|
|
1053
|
+
}
|
|
1054
|
+
return signature;
|
|
1055
|
+
}
|
|
1056
|
+
function buildInputType(typeName, jsonSchema) {
|
|
1057
|
+
const lines = [`input ${typeName} {`];
|
|
1058
|
+
if (jsonSchema.properties && typeof jsonSchema.properties === "object") {
|
|
1059
|
+
const required = new Set(
|
|
1060
|
+
Array.isArray(jsonSchema.required) ? jsonSchema.required : []
|
|
1061
|
+
);
|
|
1062
|
+
for (const [propName, propSchema] of Object.entries(
|
|
1063
|
+
jsonSchema.properties
|
|
1064
|
+
)) {
|
|
1065
|
+
const graphqlType = jsonSchemaToGraphQLType(propSchema);
|
|
1066
|
+
const isRequired = required.has(propName);
|
|
1067
|
+
lines.push(` ${propName}: ${graphqlType}${isRequired ? "!" : ""}`);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
lines.push("}");
|
|
1071
|
+
return lines.join("\n");
|
|
1072
|
+
}
|
|
1073
|
+
function buildOutputType(typeName, jsonSchema) {
|
|
1074
|
+
const lines = [`type ${typeName} {`];
|
|
1075
|
+
if (jsonSchema.properties && typeof jsonSchema.properties === "object") {
|
|
1076
|
+
const required = new Set(
|
|
1077
|
+
Array.isArray(jsonSchema.required) ? jsonSchema.required : []
|
|
1078
|
+
);
|
|
1079
|
+
for (const [propName, propSchema] of Object.entries(
|
|
1080
|
+
jsonSchema.properties
|
|
1081
|
+
)) {
|
|
1082
|
+
const graphqlType = jsonSchemaToGraphQLType(propSchema);
|
|
1083
|
+
const isRequired = required.has(propName);
|
|
1084
|
+
lines.push(` ${propName}: ${graphqlType}${isRequired ? "!" : ""}`);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
lines.push("}");
|
|
1088
|
+
return lines.join("\n");
|
|
1089
|
+
}
|
|
1090
|
+
function jsonSchemaToGraphQLType(schema) {
|
|
1091
|
+
if (!schema || typeof schema !== "object") return "JSON";
|
|
1092
|
+
if (schema.enum && Array.isArray(schema.enum)) {
|
|
1093
|
+
return "String";
|
|
1094
|
+
}
|
|
1095
|
+
const type = schema.type;
|
|
1096
|
+
switch (type) {
|
|
1097
|
+
case "string":
|
|
1098
|
+
return "String";
|
|
1099
|
+
case "integer":
|
|
1100
|
+
return "Int";
|
|
1101
|
+
case "number":
|
|
1102
|
+
return "Float";
|
|
1103
|
+
case "boolean":
|
|
1104
|
+
return "Boolean";
|
|
1105
|
+
case "array": {
|
|
1106
|
+
const items = schema.items;
|
|
1107
|
+
if (items) {
|
|
1108
|
+
return `[${jsonSchemaToGraphQLType(items)}]`;
|
|
1109
|
+
}
|
|
1110
|
+
return "[JSON]";
|
|
1111
|
+
}
|
|
1112
|
+
case "object":
|
|
1113
|
+
return "JSON";
|
|
1114
|
+
default:
|
|
1115
|
+
if (schema.oneOf || schema.anyOf || schema.allOf) {
|
|
1116
|
+
return "JSON";
|
|
1117
|
+
}
|
|
1118
|
+
if (schema.properties) {
|
|
1119
|
+
return "JSON";
|
|
1120
|
+
}
|
|
1121
|
+
return "JSON";
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
function capitalize(str) {
|
|
1125
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
1126
|
+
}
|
|
1127
|
+
function escapeSDLString(str) {
|
|
1128
|
+
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
1129
|
+
}
|
|
1130
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1131
|
+
0 && (module.exports = {
|
|
1132
|
+
collectFields,
|
|
1133
|
+
defineGraphQLSchema,
|
|
1134
|
+
defineMutation,
|
|
1135
|
+
defineQuery,
|
|
1136
|
+
defineSubscription,
|
|
1137
|
+
generateFieldId,
|
|
1138
|
+
generateGraphQLSDL,
|
|
1139
|
+
getGraphQLErrorCode,
|
|
1140
|
+
graphqlRoutes,
|
|
1141
|
+
isFieldDefinition,
|
|
1142
|
+
isSchemaDefinition,
|
|
1143
|
+
isSimpleGraphQLHandlerConfig,
|
|
1144
|
+
mapErrorToGraphQLError,
|
|
1145
|
+
mergeGraphQLSchemas,
|
|
1146
|
+
shouldMaskGraphQLError
|
|
1147
|
+
});
|
|
1148
|
+
//# sourceMappingURL=index.cjs.map
|