@fjell/express-router 4.4.76 → 4.4.78
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/CItemRouter.d.ts.map +1 -1
- package/dist/CItemRouter.js +24 -3
- package/dist/CItemRouter.js.map +2 -2
- package/dist/PItemRouter.d.ts.map +1 -1
- package/dist/PItemRouter.js +12 -3
- package/dist/PItemRouter.js.map +2 -2
- package/dist/errorHandler.d.ts.map +1 -1
- package/dist/errorHandler.js +18 -3
- package/dist/errorHandler.js.map +2 -2
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CItemRouter.d.ts","sourceRoot":"","sources":["../src/CItemRouter.ts"],"names":[],"mappings":"AAAA,OAAO,EACO,MAAM,EAAoC,IAAI,EAAqB,WAAW,EAC3F,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,OAAO,EAAiB,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAMhE,qBAAa,WAAW,CACtB,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EACrC,CAAC,SAAS,MAAM,EAChB,EAAE,SAAS,MAAM,EACjB,EAAE,SAAS,MAAM,GAAG,KAAK,EACzB,EAAE,SAAS,MAAM,GAAG,KAAK,EACzB,EAAE,SAAS,MAAM,GAAG,KAAK,EACzB,EAAE,SAAS,MAAM,GAAG,KAAK,CACzB,SAAQ,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;gBAGvC,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EACtC,IAAI,EAAE,CAAC,EACP,WAAW,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAClD,OAAO,GAAE,iBAAiB,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAM;IAKjD,SAAS,IAAI,OAAO;IAIpB,KAAK,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;IAMnD,MAAM,CAAC,GAAG,EAAE,QAAQ,GAAG,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;IAWrD,YAAY,CAAC,GAAG,EAAE,QAAQ,GAAG,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;IAI5D,UAAU,GAAU,KAAK,OAAO,EAAE,KAAK,QAAQ,
|
|
1
|
+
{"version":3,"file":"CItemRouter.d.ts","sourceRoot":"","sources":["../src/CItemRouter.ts"],"names":[],"mappings":"AAAA,OAAO,EACO,MAAM,EAAoC,IAAI,EAAqB,WAAW,EAC3F,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,OAAO,EAAiB,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAMhE,qBAAa,WAAW,CACtB,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EACrC,CAAC,SAAS,MAAM,EAChB,EAAE,SAAS,MAAM,EACjB,EAAE,SAAS,MAAM,GAAG,KAAK,EACzB,EAAE,SAAS,MAAM,GAAG,KAAK,EACzB,EAAE,SAAS,MAAM,GAAG,KAAK,EACzB,EAAE,SAAS,MAAM,GAAG,KAAK,CACzB,SAAQ,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;gBAGvC,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EACtC,IAAI,EAAE,CAAC,EACP,WAAW,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAClD,OAAO,GAAE,iBAAiB,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAM;IAKjD,SAAS,IAAI,OAAO;IAIpB,KAAK,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;IAMnD,MAAM,CAAC,GAAG,EAAE,QAAQ,GAAG,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;IAWrD,YAAY,CAAC,GAAG,EAAE,QAAQ,GAAG,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;IAI5D,UAAU,GAAU,KAAK,OAAO,EAAE,KAAK,QAAQ,mBAgDpD;IAEF,SAAS,CAAC,SAAS,GAAU,KAAK,OAAO,EAAE,KAAK,QAAQ,mBA+FtD;CAEH"}
|
package/dist/CItemRouter.js
CHANGED
|
@@ -37,11 +37,32 @@ class CItemRouter extends ItemRouter {
|
|
|
37
37
|
this.logger.default("Created Item %j", item);
|
|
38
38
|
res.status(201).json(item);
|
|
39
39
|
} catch (error) {
|
|
40
|
-
this.logger.error("Error in createItem", {
|
|
40
|
+
this.logger.error("Error in createItem endpoint", {
|
|
41
|
+
component: "CItemRouter",
|
|
42
|
+
operation: "createItem",
|
|
43
|
+
endpoint: req.path,
|
|
44
|
+
method: req.method,
|
|
45
|
+
itemType: this.getPkType(),
|
|
46
|
+
requestBody: JSON.stringify(req.body),
|
|
47
|
+
locations: JSON.stringify(this.getLocations(res)),
|
|
48
|
+
errorType: error?.constructor?.name || typeof error,
|
|
49
|
+
errorName: error?.name,
|
|
50
|
+
errorMessage: error?.message,
|
|
51
|
+
errorCode: error?.errorInfo?.code || error?.code,
|
|
52
|
+
validationErrors: error?.errorInfo?.details?.fieldErrors,
|
|
53
|
+
suggestion: "Check request body validation, required fields, parent locations, unique constraints, and data types",
|
|
54
|
+
stack: error?.stack
|
|
55
|
+
});
|
|
41
56
|
if (error.name === "CreateValidationError" || error.name === "ValidationError" || error.name === "SequelizeValidationError" || error.message && (error.message.includes("validation") || error.message.includes("required") || error.message.includes("cannot be null") || error.message.includes("notNull Violation"))) {
|
|
42
|
-
res.status(400).json({
|
|
57
|
+
res.status(400).json({
|
|
58
|
+
message: "Validation Error",
|
|
59
|
+
details: error?.errorInfo?.details?.fieldErrors || error?.message
|
|
60
|
+
});
|
|
43
61
|
} else {
|
|
44
|
-
res.status(500).json({
|
|
62
|
+
res.status(500).json({
|
|
63
|
+
message: "General Error",
|
|
64
|
+
error: error?.message
|
|
65
|
+
});
|
|
45
66
|
}
|
|
46
67
|
}
|
|
47
68
|
};
|
package/dist/CItemRouter.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/CItemRouter.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n AllOptions, ComKey, FindOperationResult, FindOptions, Item, ItemQuery, LocKey, LocKeyArray, paramsToQuery, PriKey, QueryParams, validatePK\n} from \"@fjell/core\";\nimport { Library, NotFoundError } from \"@fjell/lib\";\nimport { Request, Response } from \"express\";\nimport { ItemRouter, ItemRouterOptions } from \"./ItemRouter.js\";\n\ninterface ParsedQuery {\n [key: string]: undefined | string | string[] | ParsedQuery | ParsedQuery[];\n}\n\nexport class CItemRouter<\n T extends Item<S, L1, L2, L3, L4, L5>,\n S extends string,\n L1 extends string,\n L2 extends string = never,\n L3 extends string = never,\n L4 extends string = never,\n L5 extends string = never\n> extends ItemRouter<S, L1, L2, L3, L4, L5> {\n\n constructor(\n lib: Library<T, S, L1, L2, L3, L4, L5>,\n type: S,\n parentRoute: ItemRouter<L1, L2, L3, L4, L5, never>,\n options: ItemRouterOptions<S, L1, L2, L3, L4, L5> = {},\n ) {\n super(lib as any, type, options, parentRoute);\n }\n\n public hasParent(): boolean {\n return !!this.parentRoute;\n }\n\n public getIk(res: Response): ComKey<S, L1, L2, L3, L4, L5> {\n const pri = this.getPk(res) as PriKey<S>;\n const loc = this.getLocations(res) as LocKeyArray<L1, L2, L3, L4, L5>;\n return { kt: pri.kt, pk: pri.pk, loc }\n }\n\n public getLKA(res: Response): LocKeyArray<S, L1, L2, L3, L4> {\n /**\n * A location key array is passed to a child router to provide contextfor the items it will\n * be working with. It is always a concatenation of \"My LKA\" + \"Parent LKA\" which will\n * bubble all the way up to the root Primary.\n */\n let lka: LocKey<S | L1 | L2 | L3 | L4>[] = [this.getLk(res)];\n lka = lka.concat(this.parentRoute!.getLKA(res) as LocKey<S | L1 | L2 | L3 | L4>[]);\n return lka as LocKeyArray<S, L1, L2, L3, L4>;\n }\n\n public getLocations(res: Response): LocKeyArray<L1, L2, L3, L4, L5> {\n return this.parentRoute!.getLKA(res) as LocKeyArray<L1, L2, L3, L4, L5>;\n }\n\n public createItem = async (req: Request, res: Response) => {\n const libOperations = this.lib.operations;\n this.logger.default('Creating Item', { body: req?.body, query: req?.query, params: req?.params, locals: res?.locals });\n \n try {\n const itemToCreate = this.convertDates(req.body as Item<S, L1, L2, L3, L4, L5>);\n let item = validatePK(await libOperations.create(\n itemToCreate, { locations: this.getLocations(res) }), this.getPkType()) as Item<S, L1, L2, L3, L4, L5>;\n item = await this.postCreateItem(item);\n this.logger.default('Created Item %j', item);\n res.status(201).json(item);\n } catch (error: any) {\n this.logger.error('Error in createItem', { error });\n // Check for validation errors\n if (error.name === 'CreateValidationError' || error.name === 'ValidationError' ||\n error.name === 'SequelizeValidationError' ||\n (error.message && (error.message.includes('validation') ||\n error.message.includes('required') ||\n error.message.includes('cannot be null') ||\n error.message.includes('notNull Violation')))) {\n res.status(400).json({
|
|
5
|
-
"mappings": "AAAA;AAAA,EAC8F;AAAA,EAAoC;AAAA,OAC3H;AACP,SAAkB,qBAAqB;AAEvC,SAAS,kBAAqC;AAMvC,MAAM,oBAQH,WAAkC;AAAA,EAE1C,YACE,KACA,MACA,aACA,UAAoD,CAAC,GACrD;AACA,UAAM,KAAY,MAAM,SAAS,WAAW;AAAA,EAC9C;AAAA,EAEO,YAAqB;AAC1B,WAAO,CAAC,CAAC,KAAK;AAAA,EAChB;AAAA,EAEO,MAAM,KAA8C;AACzD,UAAM,MAAM,KAAK,MAAM,GAAG;AAC1B,UAAM,MAAM,KAAK,aAAa,GAAG;AACjC,WAAO,EAAE,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI;AAAA,EACvC;AAAA,EAEO,OAAO,KAA+C;AAM3D,QAAI,MAAuC,CAAC,KAAK,MAAM,GAAG,CAAC;AAC3D,UAAM,IAAI,OAAO,KAAK,YAAa,OAAO,GAAG,CAAoC;AACjF,WAAO;AAAA,EACT;AAAA,EAEO,aAAa,KAAgD;AAClE,WAAO,KAAK,YAAa,OAAO,GAAG;AAAA,EACrC;AAAA,EAEO,aAAa,OAAO,KAAc,QAAkB;AACzD,UAAM,gBAAgB,KAAK,IAAI;AAC/B,SAAK,OAAO,QAAQ,iBAAiB,EAAE,MAAM,KAAK,MAAM,OAAO,KAAK,OAAO,QAAQ,KAAK,QAAQ,QAAQ,KAAK,OAAO,CAAC;AAErH,QAAI;AACF,YAAM,eAAe,KAAK,aAAa,IAAI,IAAmC;AAC9E,UAAI,OAAO,WAAW,MAAM,cAAc;AAAA,QACxC;AAAA,QAAc,EAAE,WAAW,KAAK,aAAa,GAAG,EAAE;AAAA,MAAC,GAAG,KAAK,UAAU,CAAC;AACxE,aAAO,MAAM,KAAK,eAAe,IAAI;AACrC,WAAK,OAAO,QAAQ,mBAAmB,IAAI;AAC3C,UAAI,OAAO,GAAG,EAAE,KAAK,IAAI;AAAA,IAC3B,SAAS,OAAY;
|
|
4
|
+
"sourcesContent": ["import {\n AllOptions, ComKey, FindOperationResult, FindOptions, Item, ItemQuery, LocKey, LocKeyArray, paramsToQuery, PriKey, QueryParams, validatePK\n} from \"@fjell/core\";\nimport { Library, NotFoundError } from \"@fjell/lib\";\nimport { Request, Response } from \"express\";\nimport { ItemRouter, ItemRouterOptions } from \"./ItemRouter.js\";\n\ninterface ParsedQuery {\n [key: string]: undefined | string | string[] | ParsedQuery | ParsedQuery[];\n}\n\nexport class CItemRouter<\n T extends Item<S, L1, L2, L3, L4, L5>,\n S extends string,\n L1 extends string,\n L2 extends string = never,\n L3 extends string = never,\n L4 extends string = never,\n L5 extends string = never\n> extends ItemRouter<S, L1, L2, L3, L4, L5> {\n\n constructor(\n lib: Library<T, S, L1, L2, L3, L4, L5>,\n type: S,\n parentRoute: ItemRouter<L1, L2, L3, L4, L5, never>,\n options: ItemRouterOptions<S, L1, L2, L3, L4, L5> = {},\n ) {\n super(lib as any, type, options, parentRoute);\n }\n\n public hasParent(): boolean {\n return !!this.parentRoute;\n }\n\n public getIk(res: Response): ComKey<S, L1, L2, L3, L4, L5> {\n const pri = this.getPk(res) as PriKey<S>;\n const loc = this.getLocations(res) as LocKeyArray<L1, L2, L3, L4, L5>;\n return { kt: pri.kt, pk: pri.pk, loc }\n }\n\n public getLKA(res: Response): LocKeyArray<S, L1, L2, L3, L4> {\n /**\n * A location key array is passed to a child router to provide contextfor the items it will\n * be working with. It is always a concatenation of \"My LKA\" + \"Parent LKA\" which will\n * bubble all the way up to the root Primary.\n */\n let lka: LocKey<S | L1 | L2 | L3 | L4>[] = [this.getLk(res)];\n lka = lka.concat(this.parentRoute!.getLKA(res) as LocKey<S | L1 | L2 | L3 | L4>[]);\n return lka as LocKeyArray<S, L1, L2, L3, L4>;\n }\n\n public getLocations(res: Response): LocKeyArray<L1, L2, L3, L4, L5> {\n return this.parentRoute!.getLKA(res) as LocKeyArray<L1, L2, L3, L4, L5>;\n }\n\n public createItem = async (req: Request, res: Response) => {\n const libOperations = this.lib.operations;\n this.logger.default('Creating Item', { body: req?.body, query: req?.query, params: req?.params, locals: res?.locals });\n \n try {\n const itemToCreate = this.convertDates(req.body as Item<S, L1, L2, L3, L4, L5>);\n let item = validatePK(await libOperations.create(\n itemToCreate, { locations: this.getLocations(res) }), this.getPkType()) as Item<S, L1, L2, L3, L4, L5>;\n item = await this.postCreateItem(item);\n this.logger.default('Created Item %j', item);\n res.status(201).json(item);\n } catch (error: any) {\n // Log structured error details for agentic debugging\n this.logger.error('Error in createItem endpoint', {\n component: 'CItemRouter',\n operation: 'createItem',\n endpoint: req.path,\n method: req.method,\n itemType: this.getPkType(),\n requestBody: JSON.stringify(req.body),\n locations: JSON.stringify(this.getLocations(res)),\n errorType: error?.constructor?.name || typeof error,\n errorName: error?.name,\n errorMessage: error?.message,\n errorCode: error?.errorInfo?.code || error?.code,\n validationErrors: error?.errorInfo?.details?.fieldErrors,\n suggestion: 'Check request body validation, required fields, parent locations, unique constraints, and data types',\n stack: error?.stack\n });\n \n // Check for validation errors\n if (error.name === 'CreateValidationError' || error.name === 'ValidationError' ||\n error.name === 'SequelizeValidationError' ||\n (error.message && (error.message.includes('validation') ||\n error.message.includes('required') ||\n error.message.includes('cannot be null') ||\n error.message.includes('notNull Violation')))) {\n res.status(400).json({\n message: \"Validation Error\",\n details: error?.errorInfo?.details?.fieldErrors || error?.message\n });\n } else {\n res.status(500).json({\n message: \"General Error\",\n error: error?.message\n });\n }\n }\n };\n\n protected findItems = async (req: Request, res: Response) => {\n const libOperations = this.lib.operations;\n const query: ParsedQuery = req.query as unknown as ParsedQuery;\n const finder = query['finder'] as string;\n const finderParams = query['finderParams'] as string;\n const one = query['one'] as string;\n\n try {\n if (finder) {\n // If finder is defined? Call a finder.\n this.logger.default('Finding Items with Finder', { finder, finderParams, one });\n\n let parsedParams: any;\n try {\n parsedParams = finderParams ? JSON.parse(finderParams) : {};\n } catch (parseError: any) {\n res.status(400).json({\n error: 'Invalid JSON in finderParams',\n message: parseError.message\n });\n return;\n }\n\n // Parse pagination options from query parameters\n const findOptions: FindOptions | undefined =\n (req.query.limit || req.query.offset) ? {\n ...(req.query.limit && { limit: parseInt(req.query.limit as string, 10) }),\n ...(req.query.offset && { offset: parseInt(req.query.offset as string, 10) }),\n } : (void 0);\n\n const locations = this.getLocations(res);\n\n if (one === 'true') {\n const item = await (this.lib as any).findOne(finder, parsedParams, locations);\n // Wrap findOne result in FindOperationResult format\n const validatedItem = item ? (validatePK(item, this.getPkType()) as Item<S, L1, L2, L3, L4, L5>) : null;\n const result: FindOperationResult<Item<S, L1, L2, L3, L4, L5>> = {\n items: validatedItem ? [validatedItem] : [],\n metadata: {\n total: validatedItem ? 1 : 0,\n returned: validatedItem ? 1 : 0,\n offset: 0,\n hasMore: false\n }\n };\n res.json(result);\n } else {\n // Call find() with pagination options - it returns FindOperationResult\n const result = await libOperations.find(finder, parsedParams, locations, findOptions);\n \n // Validate items - validatePK can handle arrays\n const validatedItems = validatePK(result.items, this.getPkType()) as Item<S, L1, L2, L3, L4, L5>[];\n \n res.json({\n items: validatedItems,\n metadata: result.metadata\n });\n }\n } else {\n // TODO: This is once of the more important places to perform some validaation and feedback\n const itemQuery: ItemQuery = paramsToQuery(req.query as QueryParams);\n const locations = this.getLocations(res);\n this.logger.debug('Finding Items with Query: %j', itemQuery);\n this.logger.debug('Location keys being passed: %j', locations);\n \n // Parse pagination options from query params\n const allOptions: AllOptions = {};\n if (req.query.limit) {\n allOptions.limit = parseInt(req.query.limit as string, 10);\n }\n if (req.query.offset) {\n allOptions.offset = parseInt(req.query.offset as string, 10);\n }\n \n // libOperations.all() now returns AllOperationResult<V>\n const result = await libOperations.all(itemQuery, locations, allOptions);\n this.logger.debug('Found %d Items with Query', result.items.length);\n\n // Validate PKs on returned items\n const validatedItems = result.items.map((item: Item<S, L1, L2, L3, L4, L5>) => validatePK(item, this.getPkType()));\n \n // Return full AllOperationResult structure with validated items\n res.json({\n items: validatedItems,\n metadata: result.metadata\n });\n }\n } catch (error: any) {\n this.logger.error('Error in findItems', { error });\n if (error instanceof NotFoundError || error?.name === 'NotFoundError') {\n res.status(404).json({ error: error.message || 'Parent item not found' });\n } else {\n res.status(500).json({ error: error.message || 'Internal server error' });\n }\n }\n };\n\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PItemRouter.d.ts","sourceRoot":"","sources":["../src/PItemRouter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgD,IAAI,EAA4B,MAAM,EAA2B,MAAM,aAAa,CAAC;AAC5I,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,OAAO,EAAiB,MAAM,YAAY,CAAC;AAMpD,qBAAa,WAAW,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAC;gBAErE,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAE,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAM;IAI1G,KAAK,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC;IAM/B,UAAU,GAAU,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"PItemRouter.d.ts","sourceRoot":"","sources":["../src/PItemRouter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgD,IAAI,EAA4B,MAAM,EAA2B,MAAM,aAAa,CAAC;AAC5I,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,OAAO,EAAiB,MAAM,YAAY,CAAC;AAMpD,qBAAa,WAAW,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAC;gBAErE,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAE,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAM;IAI1G,KAAK,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC;IAM/B,UAAU,GAAU,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,OAAO,GAAG,mBA4EhE;IAEF,SAAS,CAAC,SAAS,GAAU,KAAK,OAAO,EAAE,KAAK,QAAQ,mBAyMtD;CAEH"}
|
package/dist/PItemRouter.js
CHANGED
|
@@ -21,14 +21,23 @@ class PItemRouter extends ItemRouter {
|
|
|
21
21
|
res.status(201).json(item);
|
|
22
22
|
} catch (error) {
|
|
23
23
|
const originalError = error?.cause || error;
|
|
24
|
-
this.logger.error("Error in createItem", {
|
|
24
|
+
this.logger.error("Error in createItem endpoint", {
|
|
25
|
+
component: "PItemRouter",
|
|
26
|
+
operation: "createItem",
|
|
27
|
+
endpoint: req.path,
|
|
28
|
+
method: req.method,
|
|
29
|
+
itemType: this.getPkType(),
|
|
30
|
+
requestBody: JSON.stringify(req.body),
|
|
25
31
|
error,
|
|
26
32
|
errorName: error?.name,
|
|
27
33
|
errorMessage: error?.message,
|
|
34
|
+
errorCode: error?.errorInfo?.code || error?.code,
|
|
28
35
|
originalErrorName: originalError?.name,
|
|
29
36
|
originalErrorMessage: originalError?.message,
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
originalErrorCode: originalError?.errorInfo?.code || originalError?.code,
|
|
38
|
+
validationErrors: error?.errorInfo?.details?.fieldErrors || originalError?.errorInfo?.details?.fieldErrors,
|
|
39
|
+
suggestion: "Check request body validation, required fields, unique constraints, and data types",
|
|
40
|
+
stack: error?.stack
|
|
32
41
|
});
|
|
33
42
|
const isValidationError = error.name === "CreateValidationError" || originalError?.name === "CreateValidationError" || error.name === "ValidationError" || originalError?.name === "ValidationError" || error.name === "SequelizeValidationError" || originalError?.name === "SequelizeValidationError" || error?.errorInfo?.code === "VALIDATION_ERROR" || originalError?.errorInfo?.code === "VALIDATION_ERROR" || error.message && (error.message.includes("validation") || error.message.includes("required") || error.message.includes("cannot be null") || error.message.includes("notNull Violation") || error.message.includes("Required field") || error.message.includes("Referenced item does not exist") || error.message.includes("Foreign key constraint") || error.message.includes("Operation failed") || error.message.includes("preCreate") || error.message.includes("preUpdate") || error.message.includes("Create Validation Failed")) || originalError?.message && (originalError.message.includes("validation") || originalError.message.includes("required") || originalError.message.includes("cannot be null") || originalError.message.includes("notNull Violation") || originalError.message.includes("Required field") || originalError.message.includes("Referenced item does not exist") || originalError.message.includes("Foreign key constraint") || originalError.message.includes("preCreate") || originalError.message.includes("preUpdate") || originalError.message.includes("Create Validation Failed"));
|
|
34
43
|
if (isValidationError) {
|
package/dist/PItemRouter.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/PItemRouter.ts"],
|
|
4
|
-
"sourcesContent": ["import { AllOptions, FindOperationResult, FindOptions, Item, ItemQuery, paramsToQuery, PriKey, QueryParams, validatePK } from \"@fjell/core\";\nimport { Request, Response } from \"express\";\nimport { ItemRouter, ItemRouterOptions } from \"./ItemRouter.js\";\nimport { Library, NotFoundError } from \"@fjell/lib\";\n\ninterface ParsedQuery {\n [key: string]: undefined | string | string[] | ParsedQuery | ParsedQuery[];\n}\n\nexport class PItemRouter<T extends Item<S>, S extends string> extends ItemRouter<S> {\n\n constructor(lib: Library<T, S>, keyType: S, options: ItemRouterOptions<S, never, never, never, never, never> = {}) {\n super(lib as any, keyType, options);\n }\n\n public getIk(res: Response): PriKey<S> {\n const pri = this.getPk(res) as PriKey<S>;\n return pri\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n public createItem = async (req: Request, res: Response, next?: any) => {\n const libOperations = this.lib.operations;\n this.logger.default('Creating Item', { body: req.body, query: req.query, params: req.params, locals: res.locals });\n\n try {\n const itemToCreate = this.convertDates(req.body as Item<S>);\n let item = validatePK(await libOperations.create(itemToCreate), this.getPkType()) as Item<S>;\n item = await this.postCreateItem(item);\n this.logger.default('Created Item %j', item);\n res.status(201).json(item);\n } catch (error: any) {\n // Check for validation errors - check multiple patterns\n // Also check error.cause since errors may be wrapped\n const originalError = error?.cause || error;\n\n // Log error details for debugging\n this.logger.error('Error in createItem', {\n error,\n errorName: error?.name,\n errorMessage: error?.message,\n originalErrorName: originalError?.name,\n originalErrorMessage: originalError?.message,\n errorCode: error?.errorInfo?.code,\n originalErrorCode: originalError?.errorInfo?.code\n });\n const isValidationError =\n error.name === 'CreateValidationError' ||\n originalError?.name === 'CreateValidationError' ||\n error.name === 'ValidationError' ||\n originalError?.name === 'ValidationError' ||\n error.name === 'SequelizeValidationError' ||\n originalError?.name === 'SequelizeValidationError' ||\n error?.errorInfo?.code === 'VALIDATION_ERROR' ||\n originalError?.errorInfo?.code === 'VALIDATION_ERROR' ||\n (error.message && (\n error.message.includes('validation') ||\n error.message.includes('required') ||\n error.message.includes('cannot be null') ||\n error.message.includes('notNull Violation') ||\n error.message.includes('Required field') ||\n error.message.includes('Referenced item does not exist') ||\n error.message.includes('Foreign key constraint') ||\n error.message.includes('Operation failed') ||\n error.message.includes('preCreate') ||\n error.message.includes('preUpdate') ||\n error.message.includes('Create Validation Failed')\n )) ||\n (originalError?.message && (\n originalError.message.includes('validation') ||\n originalError.message.includes('required') ||\n originalError.message.includes('cannot be null') ||\n originalError.message.includes('notNull Violation') ||\n originalError.message.includes('Required field') ||\n originalError.message.includes('Referenced item does not exist') ||\n originalError.message.includes('Foreign key constraint') ||\n originalError.message.includes('preCreate') ||\n originalError.message.includes('preUpdate') ||\n originalError.message.includes('Create Validation Failed')\n ));\n\n if (isValidationError) {\n const errorMessage = originalError?.message || error.message || \"Validation failed\";\n res.status(400).json({ success: false, error: errorMessage });\n } else {\n res.status(500).json({ success: false, error: error.message || \"Internal server error\" });\n }\n }\n };\n\n protected findItems = async (req: Request, res: Response) => {\n const libOperations = this.lib.operations;\n this.logger.default('Finding Items', { query: req.query, params: req.params, locals: res.locals });\n\n try {\n const query: ParsedQuery = req.query as unknown as ParsedQuery;\n const finder = query['finder'] as string;\n const finderParams = query['finderParams'] as string;\n const one = query['one'] as string;\n\n if (finder) {\n this.logger.default('Finding Items with Finder %s %j one:%s', finder, finderParams, one);\n\n let parsedParams: any;\n try {\n parsedParams = finderParams ? JSON.parse(finderParams) : {};\n } catch (parseError: any) {\n res.status(400).json({\n error: 'Invalid JSON in finderParams',\n message: parseError.message\n });\n return;\n }\n\n // Parse pagination options from query parameters\n const findOptions: FindOptions | undefined =\n (req.query.limit || req.query.offset) ? {\n ...(req.query.limit && { limit: parseInt(req.query.limit as string, 10) }),\n ...(req.query.offset && { offset: parseInt(req.query.offset as string, 10) }),\n } : (void 0);\n\n if (one === 'true') {\n const item = await (this.lib as any).findOne(finder, parsedParams);\n // Wrap findOne result in FindOperationResult format\n const validatedItem = item ? (validatePK(item, this.getPkType()) as Item<S>) : null;\n const result: FindOperationResult<Item<S>> = {\n items: validatedItem ? [validatedItem] : [],\n metadata: {\n total: validatedItem ? 1 : 0,\n returned: validatedItem ? 1 : 0,\n offset: 0,\n hasMore: false\n }\n };\n res.json(result);\n } else {\n\n let result: FindOperationResult<Item<S>>;\n try {\n result = await libOperations.find(finder, parsedParams, [], findOptions);\n } catch (findError: any) {\n console.error('=== ERROR IN libOperations.find ===');\n console.error('Error:', findError);\n console.error('Error Message:', findError?.message);\n console.error('Error Stack:', findError?.stack);\n this.logger.error('Error calling libOperations.find', {\n finder,\n parsedParams,\n findOptions,\n errorMessage: findError?.message,\n errorName: findError?.name,\n errorStack: findError?.stack,\n errorCause: findError?.cause\n });\n throw findError;\n }\n\n let validatedItems: Item<S>[];\n try {\n validatedItems = validatePK(result.items, this.getPkType()) as Item<S>[];\n } catch (validationError: any) {\n console.error('=== ERROR IN VALIDATION ===');\n console.error('Validation Error:', validationError);\n console.error('Error Message:', validationError?.message);\n console.error('Error Stack:', validationError?.stack);\n this.logger.error('Error validating items from find result', {\n finder,\n itemCount: result.items?.length,\n firstItem: result.items?.[0],\n errorMessage: validationError?.message,\n errorName: validationError?.name,\n errorStack: validationError?.stack\n });\n throw validationError;\n }\n\n res.json({\n items: validatedItems,\n metadata: result.metadata\n });\n\n }\n } else {\n // TODO: This is once of the more important places to perform some validaation and feedback\n const itemQuery: ItemQuery = paramsToQuery(req.query as QueryParams);\n this.logger.default('Finding Items with a query %j', itemQuery);\n\n // Parse pagination options from query params\n const allOptions: AllOptions = {};\n if (req.query.limit) {\n allOptions.limit = parseInt(req.query.limit as string, 10);\n }\n if (req.query.offset) {\n allOptions.offset = parseInt(req.query.offset as string, 10);\n }\n\n // libOperations.all() now returns AllOperationResult<V>\n const result = await libOperations.all(itemQuery, [], allOptions);\n\n // Validate PKs on returned items\n const validatedItems = result.items.map((item: Item<S>) => validatePK(item, this.getPkType()));\n\n // Return full AllOperationResult structure with validated items\n res.json({\n items: validatedItems,\n metadata: result.metadata\n });\n }\n } catch (error: any) {\n // Enhanced error logging to capture the actual error details\n const originalError = error?.cause || error;\n const errorMessage = error?.message || originalError?.message || String(error) || 'Internal server error';\n // Extract all error properties that can be serialized\n const errorProps: Record<string, any> = {};\n if (error) {\n Object.getOwnPropertyNames(error).forEach(key => {\n try {\n const value = (error as any)[key];\n // Only include serializable values\n if (typeof value !== 'function' && typeof value !== 'object') {\n errorProps[key] = value;\n } else if (key === 'stack' || key === 'message' || key === 'cause') {\n errorProps[key] = value;\n }\n } catch {\n // Skip properties that can't be accessed\n }\n });\n }\n\n const errorDetails = {\n errorMessage,\n errorName: error?.name || originalError?.name,\n errorCode: error?.code || originalError?.code,\n errorStack: error?.stack || originalError?.stack,\n errorString: String(error),\n errorType: error?.constructor?.name,\n errorProps,\n errorCause: error?.cause ? {\n message: error.cause?.message,\n name: error.cause?.name,\n stack: error.cause?.stack\n } : void 0,\n finder: (req.query as any)?.finder,\n finderParams: (req.query as any)?.finderParams,\n requestPath: req.path,\n requestMethod: req.method\n };\n\n // Log with multiple levels to ensure we see it\n // Serialize error properly - errors don't serialize well, so log each property separately\n console.error('=== ERROR IN findItems ===');\n console.error('Error object:', error);\n console.error('Error message:', errorMessage);\n console.error('Error name:', error?.name);\n console.error('Error code:', error?.code);\n console.error('Error stack:', error?.stack);\n console.error('Error cause:', error?.cause);\n console.error('Error string:', String(error));\n console.error('Full error details:', JSON.stringify(errorDetails, null, 2));\n console.error('=== END ERROR ===\\n\\n');\n\n // Log to structured logger with serializable data only (don't pass errorDetails directly)\n this.logger.error('Error in findItems', {\n errorMessage,\n errorName: errorDetails.errorName,\n errorCode: errorDetails.errorCode,\n errorStack: errorDetails.errorStack,\n errorType: errorDetails.errorType,\n errorProps: errorDetails.errorProps,\n errorCause: errorDetails.errorCause,\n finder: errorDetails.finder,\n finderParams: errorDetails.finderParams,\n requestPath: errorDetails.requestPath,\n requestMethod: errorDetails.requestMethod\n });\n\n if (error instanceof NotFoundError || error?.name === 'NotFoundError') {\n res.status(404).json({ error: errorMessage });\n } else {\n // Include more details in development, but keep it generic in production\n const isDevelopment = process.env.NODE_ENV === 'development';\n res.status(500).json({\n error: errorMessage,\n ...(isDevelopment && {\n details: errorDetails.errorName,\n stack: errorDetails.errorStack\n })\n });\n }\n }\n };\n\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAwE,eAAoC,kBAAkB;AAE9H,SAAS,kBAAqC;AAC9C,SAAkB,qBAAqB;AAMhC,MAAM,oBAAyD,WAAc;AAAA,EAElF,YAAY,KAAoB,SAAY,UAAmE,CAAC,GAAG;AACjH,UAAM,KAAY,SAAS,OAAO;AAAA,EACpC;AAAA,EAEO,MAAM,KAA0B;AACrC,UAAM,MAAM,KAAK,MAAM,GAAG;AAC1B,WAAO;AAAA,EACT;AAAA;AAAA,EAGO,aAAa,OAAO,KAAc,KAAe,SAAe;AACrE,UAAM,gBAAgB,KAAK,IAAI;AAC/B,SAAK,OAAO,QAAQ,iBAAiB,EAAE,MAAM,IAAI,MAAM,OAAO,IAAI,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,OAAO,CAAC;AAEjH,QAAI;AACF,YAAM,eAAe,KAAK,aAAa,IAAI,IAAe;AAC1D,UAAI,OAAO,WAAW,MAAM,cAAc,OAAO,YAAY,GAAG,KAAK,UAAU,CAAC;AAChF,aAAO,MAAM,KAAK,eAAe,IAAI;AACrC,WAAK,OAAO,QAAQ,mBAAmB,IAAI;AAC3C,UAAI,OAAO,GAAG,EAAE,KAAK,IAAI;AAAA,IAC3B,SAAS,OAAY;AAGnB,YAAM,gBAAgB,OAAO,SAAS;AAGtC,WAAK,OAAO,MAAM,
|
|
4
|
+
"sourcesContent": ["import { AllOptions, FindOperationResult, FindOptions, Item, ItemQuery, paramsToQuery, PriKey, QueryParams, validatePK } from \"@fjell/core\";\nimport { Request, Response } from \"express\";\nimport { ItemRouter, ItemRouterOptions } from \"./ItemRouter.js\";\nimport { Library, NotFoundError } from \"@fjell/lib\";\n\ninterface ParsedQuery {\n [key: string]: undefined | string | string[] | ParsedQuery | ParsedQuery[];\n}\n\nexport class PItemRouter<T extends Item<S>, S extends string> extends ItemRouter<S> {\n\n constructor(lib: Library<T, S>, keyType: S, options: ItemRouterOptions<S, never, never, never, never, never> = {}) {\n super(lib as any, keyType, options);\n }\n\n public getIk(res: Response): PriKey<S> {\n const pri = this.getPk(res) as PriKey<S>;\n return pri\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n public createItem = async (req: Request, res: Response, next?: any) => {\n const libOperations = this.lib.operations;\n this.logger.default('Creating Item', { body: req.body, query: req.query, params: req.params, locals: res.locals });\n\n try {\n const itemToCreate = this.convertDates(req.body as Item<S>);\n let item = validatePK(await libOperations.create(itemToCreate), this.getPkType()) as Item<S>;\n item = await this.postCreateItem(item);\n this.logger.default('Created Item %j', item);\n res.status(201).json(item);\n } catch (error: any) {\n // Check for validation errors - check multiple patterns\n // Also check error.cause since errors may be wrapped\n const originalError = error?.cause || error;\n\n // Log structured error details for agentic debugging\n this.logger.error('Error in createItem endpoint', {\n component: 'PItemRouter',\n operation: 'createItem',\n endpoint: req.path,\n method: req.method,\n itemType: this.getPkType(),\n requestBody: JSON.stringify(req.body),\n error,\n errorName: error?.name,\n errorMessage: error?.message,\n errorCode: error?.errorInfo?.code || error?.code,\n originalErrorName: originalError?.name,\n originalErrorMessage: originalError?.message,\n originalErrorCode: originalError?.errorInfo?.code || originalError?.code,\n validationErrors: error?.errorInfo?.details?.fieldErrors || originalError?.errorInfo?.details?.fieldErrors,\n suggestion: 'Check request body validation, required fields, unique constraints, and data types',\n stack: error?.stack\n });\n const isValidationError =\n error.name === 'CreateValidationError' ||\n originalError?.name === 'CreateValidationError' ||\n error.name === 'ValidationError' ||\n originalError?.name === 'ValidationError' ||\n error.name === 'SequelizeValidationError' ||\n originalError?.name === 'SequelizeValidationError' ||\n error?.errorInfo?.code === 'VALIDATION_ERROR' ||\n originalError?.errorInfo?.code === 'VALIDATION_ERROR' ||\n (error.message && (\n error.message.includes('validation') ||\n error.message.includes('required') ||\n error.message.includes('cannot be null') ||\n error.message.includes('notNull Violation') ||\n error.message.includes('Required field') ||\n error.message.includes('Referenced item does not exist') ||\n error.message.includes('Foreign key constraint') ||\n error.message.includes('Operation failed') ||\n error.message.includes('preCreate') ||\n error.message.includes('preUpdate') ||\n error.message.includes('Create Validation Failed')\n )) ||\n (originalError?.message && (\n originalError.message.includes('validation') ||\n originalError.message.includes('required') ||\n originalError.message.includes('cannot be null') ||\n originalError.message.includes('notNull Violation') ||\n originalError.message.includes('Required field') ||\n originalError.message.includes('Referenced item does not exist') ||\n originalError.message.includes('Foreign key constraint') ||\n originalError.message.includes('preCreate') ||\n originalError.message.includes('preUpdate') ||\n originalError.message.includes('Create Validation Failed')\n ));\n\n if (isValidationError) {\n const errorMessage = originalError?.message || error.message || \"Validation failed\";\n res.status(400).json({ success: false, error: errorMessage });\n } else {\n res.status(500).json({ success: false, error: error.message || \"Internal server error\" });\n }\n }\n };\n\n protected findItems = async (req: Request, res: Response) => {\n const libOperations = this.lib.operations;\n this.logger.default('Finding Items', { query: req.query, params: req.params, locals: res.locals });\n\n try {\n const query: ParsedQuery = req.query as unknown as ParsedQuery;\n const finder = query['finder'] as string;\n const finderParams = query['finderParams'] as string;\n const one = query['one'] as string;\n\n if (finder) {\n this.logger.default('Finding Items with Finder %s %j one:%s', finder, finderParams, one);\n\n let parsedParams: any;\n try {\n parsedParams = finderParams ? JSON.parse(finderParams) : {};\n } catch (parseError: any) {\n res.status(400).json({\n error: 'Invalid JSON in finderParams',\n message: parseError.message\n });\n return;\n }\n\n // Parse pagination options from query parameters\n const findOptions: FindOptions | undefined =\n (req.query.limit || req.query.offset) ? {\n ...(req.query.limit && { limit: parseInt(req.query.limit as string, 10) }),\n ...(req.query.offset && { offset: parseInt(req.query.offset as string, 10) }),\n } : (void 0);\n\n if (one === 'true') {\n const item = await (this.lib as any).findOne(finder, parsedParams);\n // Wrap findOne result in FindOperationResult format\n const validatedItem = item ? (validatePK(item, this.getPkType()) as Item<S>) : null;\n const result: FindOperationResult<Item<S>> = {\n items: validatedItem ? [validatedItem] : [],\n metadata: {\n total: validatedItem ? 1 : 0,\n returned: validatedItem ? 1 : 0,\n offset: 0,\n hasMore: false\n }\n };\n res.json(result);\n } else {\n\n let result: FindOperationResult<Item<S>>;\n try {\n result = await libOperations.find(finder, parsedParams, [], findOptions);\n } catch (findError: any) {\n console.error('=== ERROR IN libOperations.find ===');\n console.error('Error:', findError);\n console.error('Error Message:', findError?.message);\n console.error('Error Stack:', findError?.stack);\n this.logger.error('Error calling libOperations.find', {\n finder,\n parsedParams,\n findOptions,\n errorMessage: findError?.message,\n errorName: findError?.name,\n errorStack: findError?.stack,\n errorCause: findError?.cause\n });\n throw findError;\n }\n\n let validatedItems: Item<S>[];\n try {\n validatedItems = validatePK(result.items, this.getPkType()) as Item<S>[];\n } catch (validationError: any) {\n console.error('=== ERROR IN VALIDATION ===');\n console.error('Validation Error:', validationError);\n console.error('Error Message:', validationError?.message);\n console.error('Error Stack:', validationError?.stack);\n this.logger.error('Error validating items from find result', {\n finder,\n itemCount: result.items?.length,\n firstItem: result.items?.[0],\n errorMessage: validationError?.message,\n errorName: validationError?.name,\n errorStack: validationError?.stack\n });\n throw validationError;\n }\n\n res.json({\n items: validatedItems,\n metadata: result.metadata\n });\n\n }\n } else {\n // TODO: This is once of the more important places to perform some validaation and feedback\n const itemQuery: ItemQuery = paramsToQuery(req.query as QueryParams);\n this.logger.default('Finding Items with a query %j', itemQuery);\n\n // Parse pagination options from query params\n const allOptions: AllOptions = {};\n if (req.query.limit) {\n allOptions.limit = parseInt(req.query.limit as string, 10);\n }\n if (req.query.offset) {\n allOptions.offset = parseInt(req.query.offset as string, 10);\n }\n\n // libOperations.all() now returns AllOperationResult<V>\n const result = await libOperations.all(itemQuery, [], allOptions);\n\n // Validate PKs on returned items\n const validatedItems = result.items.map((item: Item<S>) => validatePK(item, this.getPkType()));\n\n // Return full AllOperationResult structure with validated items\n res.json({\n items: validatedItems,\n metadata: result.metadata\n });\n }\n } catch (error: any) {\n // Enhanced error logging to capture the actual error details\n const originalError = error?.cause || error;\n const errorMessage = error?.message || originalError?.message || String(error) || 'Internal server error';\n // Extract all error properties that can be serialized\n const errorProps: Record<string, any> = {};\n if (error) {\n Object.getOwnPropertyNames(error).forEach(key => {\n try {\n const value = (error as any)[key];\n // Only include serializable values\n if (typeof value !== 'function' && typeof value !== 'object') {\n errorProps[key] = value;\n } else if (key === 'stack' || key === 'message' || key === 'cause') {\n errorProps[key] = value;\n }\n } catch {\n // Skip properties that can't be accessed\n }\n });\n }\n\n const errorDetails = {\n errorMessage,\n errorName: error?.name || originalError?.name,\n errorCode: error?.code || originalError?.code,\n errorStack: error?.stack || originalError?.stack,\n errorString: String(error),\n errorType: error?.constructor?.name,\n errorProps,\n errorCause: error?.cause ? {\n message: error.cause?.message,\n name: error.cause?.name,\n stack: error.cause?.stack\n } : void 0,\n finder: (req.query as any)?.finder,\n finderParams: (req.query as any)?.finderParams,\n requestPath: req.path,\n requestMethod: req.method\n };\n\n // Log with multiple levels to ensure we see it\n // Serialize error properly - errors don't serialize well, so log each property separately\n console.error('=== ERROR IN findItems ===');\n console.error('Error object:', error);\n console.error('Error message:', errorMessage);\n console.error('Error name:', error?.name);\n console.error('Error code:', error?.code);\n console.error('Error stack:', error?.stack);\n console.error('Error cause:', error?.cause);\n console.error('Error string:', String(error));\n console.error('Full error details:', JSON.stringify(errorDetails, null, 2));\n console.error('=== END ERROR ===\\n\\n');\n\n // Log to structured logger with serializable data only (don't pass errorDetails directly)\n this.logger.error('Error in findItems', {\n errorMessage,\n errorName: errorDetails.errorName,\n errorCode: errorDetails.errorCode,\n errorStack: errorDetails.errorStack,\n errorType: errorDetails.errorType,\n errorProps: errorDetails.errorProps,\n errorCause: errorDetails.errorCause,\n finder: errorDetails.finder,\n finderParams: errorDetails.finderParams,\n requestPath: errorDetails.requestPath,\n requestMethod: errorDetails.requestMethod\n });\n\n if (error instanceof NotFoundError || error?.name === 'NotFoundError') {\n res.status(404).json({ error: errorMessage });\n } else {\n // Include more details in development, but keep it generic in production\n const isDevelopment = process.env.NODE_ENV === 'development';\n res.status(500).json({\n error: errorMessage,\n ...(isDevelopment && {\n details: errorDetails.errorName,\n stack: errorDetails.errorStack\n })\n });\n }\n }\n };\n\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../src/errorHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAiB,MAAM,aAAa,CAAC;AAKvD,MAAM,WAAW,mBAAmB;IAClC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,SAAS,GAAG,IAAI,CAAC;CACtD;AAED,qBAAa,iBAAiB;IAChB,OAAO,CAAC,OAAO;gBAAP,OAAO,GAAE,mBAAwB;IAQrD;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAgB7B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAiE1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAoBxB;;OAEG;IACH,OAAO,CAAC,2BAA2B;IA4BnC;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB;;OAEG;IACH,OAAO,CAAC,YAAY;IAMpB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAc7B;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAclC;;OAEG;IACH,MAAM,GAAI,KAAK,GAAG,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../src/errorHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAiB,MAAM,aAAa,CAAC;AAKvD,MAAM,WAAW,mBAAmB;IAClC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,SAAS,GAAG,IAAI,CAAC;CACtD;AAED,qBAAa,iBAAiB;IAChB,OAAO,CAAC,OAAO;gBAAP,OAAO,GAAE,mBAAwB;IAQrD;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAgB7B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAiE1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAoBxB;;OAEG;IACH,OAAO,CAAC,2BAA2B;IA4BnC;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB;;OAEG;IACH,OAAO,CAAC,YAAY;IAMpB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAc7B;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAclC;;OAEG;IACH,MAAM,GAAI,KAAK,GAAG,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,IAAI,CAqDxE;CACH;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,mBAAmB,SA3D/C,GAAG,OAAO,OAAO,OAAO,QAAQ,QAAQ,YAAY,KAAG,IAAI,CA8D3E"}
|
package/dist/errorHandler.js
CHANGED
|
@@ -191,8 +191,21 @@ class FjellErrorHandler {
|
|
|
191
191
|
statusCode = this.getHttpStatusForError(errorInfo.code);
|
|
192
192
|
}
|
|
193
193
|
if (this.options.logErrors) {
|
|
194
|
-
logger.error("
|
|
195
|
-
|
|
194
|
+
logger.error("HTTP request error", {
|
|
195
|
+
component: "FjellErrorHandler",
|
|
196
|
+
operation: "handle",
|
|
197
|
+
endpoint: req.path,
|
|
198
|
+
method: req.method,
|
|
199
|
+
statusCode,
|
|
200
|
+
errorCode: errorInfo.code,
|
|
201
|
+
errorMessage: errorInfo.message,
|
|
202
|
+
operationType: errorInfo.operation?.type,
|
|
203
|
+
operationName: errorInfo.operation?.name,
|
|
204
|
+
itemType: errorInfo.context?.itemType,
|
|
205
|
+
requestId: this.getRequestId(req),
|
|
206
|
+
validOptions: errorInfo.details?.validOptions,
|
|
207
|
+
suggestedAction: errorInfo.details?.suggestedAction,
|
|
208
|
+
retryable: errorInfo.details?.retryable,
|
|
196
209
|
request: {
|
|
197
210
|
method: req.method,
|
|
198
211
|
path: req.path,
|
|
@@ -200,7 +213,9 @@ class FjellErrorHandler {
|
|
|
200
213
|
body: req.body,
|
|
201
214
|
query: req.query,
|
|
202
215
|
params: req.params
|
|
203
|
-
}
|
|
216
|
+
},
|
|
217
|
+
suggestion: errorInfo.details?.suggestedAction || "Check request parameters, authentication, and data validation",
|
|
218
|
+
errorInfo
|
|
204
219
|
});
|
|
205
220
|
}
|
|
206
221
|
res.status(statusCode).json({
|
package/dist/errorHandler.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/errorHandler.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextFunction, Request, Response } from 'express';\nimport { ErrorInfo, isActionError } from '@fjell/core';\nimport LibLogger from './logger.js';\n\nconst logger = LibLogger.get('express-router', 'errorHandler');\n\nexport interface ErrorHandlerOptions {\n includeStackTrace?: boolean;\n logErrors?: boolean;\n customErrorMapper?: (error: any) => ErrorInfo | null;\n}\n\nexport class FjellErrorHandler {\n constructor(private options: ErrorHandlerOptions = {}) {\n this.options = {\n includeStackTrace: process.env.NODE_ENV === 'development',\n logErrors: true,\n ...options\n };\n }\n\n /**\n * Get appropriate HTTP status code for error\n */\n private getHttpStatusForError(code: string): number {\n const statusMap: Record<string, number> = {\n 'VALIDATION_ERROR': 400,\n 'NOT_FOUND': 404,\n 'PERMISSION_ERROR': 403,\n 'UNAUTHORIZED': 401,\n 'BUSINESS_LOGIC_ERROR': 422,\n 'ACTION_NOT_FOUND': 404,\n 'DUPLICATE_ERROR': 409,\n 'RATE_LIMIT_EXCEEDED': 429,\n 'INTERNAL_ERROR': 500,\n };\n\n return statusMap[code] || 500;\n }\n\n /**\n * Convert generic errors to ErrorInfo structure\n */\n private convertToErrorInfo(error: any, req: Request): ErrorInfo {\n // Check if custom mapper can handle this error\n if (this.options.customErrorMapper) {\n const mapped = this.options.customErrorMapper(error);\n if (mapped) {\n return this.enhanceErrorInfo(mapped, req);\n }\n }\n\n // Handle common HTTP errors\n if (error.status || error.statusCode) {\n const status = error.status || error.statusCode;\n const technical: ErrorInfo['technical'] = {\n timestamp: new Date().toISOString(),\n requestId: this.getRequestId(req)\n };\n \n if (this.options.includeStackTrace && error.stack) {\n technical.stackTrace = error.stack;\n }\n\n return {\n code: this.getErrorCodeForStatus(status),\n message: error.message || this.getDefaultMessageForStatus(status),\n operation: {\n type: this.getOperationTypeFromRequest(req),\n name: req.path,\n params: { ...req.body, ...req.query, ...req.params }\n },\n context: {\n itemType: this.extractItemType(req.path)\n },\n technical\n };\n }\n\n // Default error structure\n const technical: ErrorInfo['technical'] = {\n timestamp: new Date().toISOString(),\n requestId: this.getRequestId(req)\n };\n \n if (this.options.includeStackTrace && error.stack) {\n technical.stackTrace = error.stack;\n }\n \n if (error.cause) {\n technical.cause = error.cause;\n }\n\n return {\n code: 'INTERNAL_ERROR',\n message: error.message || 'An unexpected error occurred',\n operation: {\n type: this.getOperationTypeFromRequest(req),\n name: req.path,\n params: { ...req.body, ...req.query, ...req.params }\n },\n context: {\n itemType: this.extractItemType(req.path)\n },\n technical\n };\n }\n\n /**\n * Enhance error info with request context\n */\n private enhanceErrorInfo(errorInfo: ErrorInfo, req: Request): ErrorInfo {\n const technical: ErrorInfo['technical'] = {\n timestamp: errorInfo.technical?.timestamp || new Date().toISOString(),\n requestId: this.getRequestId(req)\n };\n\n if (this.options.includeStackTrace && errorInfo.technical?.stackTrace) {\n technical.stackTrace = errorInfo.technical.stackTrace;\n }\n\n if (errorInfo.technical?.cause) {\n technical.cause = errorInfo.technical.cause;\n }\n\n return {\n ...errorInfo,\n technical\n };\n }\n\n /**\n * Extract operation type from request\n */\n private getOperationTypeFromRequest(req: Request): ErrorInfo['operation']['type'] {\n const path = req.path.toLowerCase();\n const method = req.method.toLowerCase();\n\n // Pattern matching for operation types\n if (path.includes('/action/')) return 'action';\n if (path.includes('/allaction/')) return 'allAction';\n if (path.includes('/facet/')) return 'facet';\n if (path.includes('/allfacet/')) return 'allFacet';\n if (path.includes('/find/')) return method === 'get' ? 'findOne' : 'find';\n if (path.includes('/upsert')) return 'upsert';\n \n // Method-based detection\n switch (method) {\n case 'get':\n return path.match(/\\/\\d+$/) ? 'get' : 'all';\n case 'post':\n return 'create';\n case 'put':\n case 'patch':\n return 'update';\n case 'delete':\n return 'remove';\n default:\n return 'get';\n }\n }\n\n /**\n * Extract item type from path\n */\n private extractItemType(path: string): string {\n const segments = path.split('/').filter(s => s && !s.match(/^\\d+$/));\n return segments[segments.length - 1] || 'unknown';\n }\n\n /**\n * Get request ID from headers or generate one\n */\n private getRequestId(req: Request): string {\n return (req.headers['x-request-id'] ||\n req.headers['x-trace-id'] ||\n `req-${Date.now()}`) as string;\n }\n\n /**\n * Get error code for HTTP status\n */\n private getErrorCodeForStatus(status: number): string {\n const codeMap: Record<number, string> = {\n 400: 'VALIDATION_ERROR',\n 401: 'UNAUTHORIZED',\n 403: 'PERMISSION_ERROR',\n 404: 'NOT_FOUND',\n 409: 'DUPLICATE_ERROR',\n 422: 'BUSINESS_LOGIC_ERROR',\n 429: 'RATE_LIMIT_EXCEEDED',\n 500: 'INTERNAL_ERROR',\n };\n return codeMap[status] || 'INTERNAL_ERROR';\n }\n\n /**\n * Get default message for HTTP status\n */\n private getDefaultMessageForStatus(status: number): string {\n const messageMap: Record<number, string> = {\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 403: 'Forbidden',\n 404: 'Not Found',\n 409: 'Conflict',\n 422: 'Unprocessable Entity',\n 429: 'Too Many Requests',\n 500: 'Internal Server Error',\n };\n return messageMap[status] || 'Error';\n }\n\n /**\n * Express error handler middleware\n */\n handle = (err: any, req: Request, res: Response, next: NextFunction): void => {\n if (res.headersSent) {\n return next(err);\n }\n\n let errorInfo: ErrorInfo;\n let statusCode: number;\n\n if (isActionError(err)) {\n // This is already a structured Fjell error\n errorInfo = this.enhanceErrorInfo(err.errorInfo, req);\n statusCode = this.getHttpStatusForError(errorInfo.code);\n } else {\n // Convert to ErrorInfo structure\n errorInfo = this.convertToErrorInfo(err, req);\n statusCode = this.getHttpStatusForError(errorInfo.code);\n }\n\n // Log the error if configured\n if (this.options.logErrors) {\n logger.error('
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { NextFunction, Request, Response } from 'express';\nimport { ErrorInfo, isActionError } from '@fjell/core';\nimport LibLogger from './logger.js';\n\nconst logger = LibLogger.get('express-router', 'errorHandler');\n\nexport interface ErrorHandlerOptions {\n includeStackTrace?: boolean;\n logErrors?: boolean;\n customErrorMapper?: (error: any) => ErrorInfo | null;\n}\n\nexport class FjellErrorHandler {\n constructor(private options: ErrorHandlerOptions = {}) {\n this.options = {\n includeStackTrace: process.env.NODE_ENV === 'development',\n logErrors: true,\n ...options\n };\n }\n\n /**\n * Get appropriate HTTP status code for error\n */\n private getHttpStatusForError(code: string): number {\n const statusMap: Record<string, number> = {\n 'VALIDATION_ERROR': 400,\n 'NOT_FOUND': 404,\n 'PERMISSION_ERROR': 403,\n 'UNAUTHORIZED': 401,\n 'BUSINESS_LOGIC_ERROR': 422,\n 'ACTION_NOT_FOUND': 404,\n 'DUPLICATE_ERROR': 409,\n 'RATE_LIMIT_EXCEEDED': 429,\n 'INTERNAL_ERROR': 500,\n };\n\n return statusMap[code] || 500;\n }\n\n /**\n * Convert generic errors to ErrorInfo structure\n */\n private convertToErrorInfo(error: any, req: Request): ErrorInfo {\n // Check if custom mapper can handle this error\n if (this.options.customErrorMapper) {\n const mapped = this.options.customErrorMapper(error);\n if (mapped) {\n return this.enhanceErrorInfo(mapped, req);\n }\n }\n\n // Handle common HTTP errors\n if (error.status || error.statusCode) {\n const status = error.status || error.statusCode;\n const technical: ErrorInfo['technical'] = {\n timestamp: new Date().toISOString(),\n requestId: this.getRequestId(req)\n };\n \n if (this.options.includeStackTrace && error.stack) {\n technical.stackTrace = error.stack;\n }\n\n return {\n code: this.getErrorCodeForStatus(status),\n message: error.message || this.getDefaultMessageForStatus(status),\n operation: {\n type: this.getOperationTypeFromRequest(req),\n name: req.path,\n params: { ...req.body, ...req.query, ...req.params }\n },\n context: {\n itemType: this.extractItemType(req.path)\n },\n technical\n };\n }\n\n // Default error structure\n const technical: ErrorInfo['technical'] = {\n timestamp: new Date().toISOString(),\n requestId: this.getRequestId(req)\n };\n \n if (this.options.includeStackTrace && error.stack) {\n technical.stackTrace = error.stack;\n }\n \n if (error.cause) {\n technical.cause = error.cause;\n }\n\n return {\n code: 'INTERNAL_ERROR',\n message: error.message || 'An unexpected error occurred',\n operation: {\n type: this.getOperationTypeFromRequest(req),\n name: req.path,\n params: { ...req.body, ...req.query, ...req.params }\n },\n context: {\n itemType: this.extractItemType(req.path)\n },\n technical\n };\n }\n\n /**\n * Enhance error info with request context\n */\n private enhanceErrorInfo(errorInfo: ErrorInfo, req: Request): ErrorInfo {\n const technical: ErrorInfo['technical'] = {\n timestamp: errorInfo.technical?.timestamp || new Date().toISOString(),\n requestId: this.getRequestId(req)\n };\n\n if (this.options.includeStackTrace && errorInfo.technical?.stackTrace) {\n technical.stackTrace = errorInfo.technical.stackTrace;\n }\n\n if (errorInfo.technical?.cause) {\n technical.cause = errorInfo.technical.cause;\n }\n\n return {\n ...errorInfo,\n technical\n };\n }\n\n /**\n * Extract operation type from request\n */\n private getOperationTypeFromRequest(req: Request): ErrorInfo['operation']['type'] {\n const path = req.path.toLowerCase();\n const method = req.method.toLowerCase();\n\n // Pattern matching for operation types\n if (path.includes('/action/')) return 'action';\n if (path.includes('/allaction/')) return 'allAction';\n if (path.includes('/facet/')) return 'facet';\n if (path.includes('/allfacet/')) return 'allFacet';\n if (path.includes('/find/')) return method === 'get' ? 'findOne' : 'find';\n if (path.includes('/upsert')) return 'upsert';\n \n // Method-based detection\n switch (method) {\n case 'get':\n return path.match(/\\/\\d+$/) ? 'get' : 'all';\n case 'post':\n return 'create';\n case 'put':\n case 'patch':\n return 'update';\n case 'delete':\n return 'remove';\n default:\n return 'get';\n }\n }\n\n /**\n * Extract item type from path\n */\n private extractItemType(path: string): string {\n const segments = path.split('/').filter(s => s && !s.match(/^\\d+$/));\n return segments[segments.length - 1] || 'unknown';\n }\n\n /**\n * Get request ID from headers or generate one\n */\n private getRequestId(req: Request): string {\n return (req.headers['x-request-id'] ||\n req.headers['x-trace-id'] ||\n `req-${Date.now()}`) as string;\n }\n\n /**\n * Get error code for HTTP status\n */\n private getErrorCodeForStatus(status: number): string {\n const codeMap: Record<number, string> = {\n 400: 'VALIDATION_ERROR',\n 401: 'UNAUTHORIZED',\n 403: 'PERMISSION_ERROR',\n 404: 'NOT_FOUND',\n 409: 'DUPLICATE_ERROR',\n 422: 'BUSINESS_LOGIC_ERROR',\n 429: 'RATE_LIMIT_EXCEEDED',\n 500: 'INTERNAL_ERROR',\n };\n return codeMap[status] || 'INTERNAL_ERROR';\n }\n\n /**\n * Get default message for HTTP status\n */\n private getDefaultMessageForStatus(status: number): string {\n const messageMap: Record<number, string> = {\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 403: 'Forbidden',\n 404: 'Not Found',\n 409: 'Conflict',\n 422: 'Unprocessable Entity',\n 429: 'Too Many Requests',\n 500: 'Internal Server Error',\n };\n return messageMap[status] || 'Error';\n }\n\n /**\n * Express error handler middleware\n */\n handle = (err: any, req: Request, res: Response, next: NextFunction): void => {\n if (res.headersSent) {\n return next(err);\n }\n\n let errorInfo: ErrorInfo;\n let statusCode: number;\n\n if (isActionError(err)) {\n // This is already a structured Fjell error\n errorInfo = this.enhanceErrorInfo(err.errorInfo, req);\n statusCode = this.getHttpStatusForError(errorInfo.code);\n } else {\n // Convert to ErrorInfo structure\n errorInfo = this.convertToErrorInfo(err, req);\n statusCode = this.getHttpStatusForError(errorInfo.code);\n }\n\n // Log the error if configured - structured for agentic debugging\n if (this.options.logErrors) {\n logger.error('HTTP request error', {\n component: 'FjellErrorHandler',\n operation: 'handle',\n endpoint: req.path,\n method: req.method,\n statusCode,\n errorCode: errorInfo.code,\n errorMessage: errorInfo.message,\n operationType: errorInfo.operation?.type,\n operationName: errorInfo.operation?.name,\n itemType: errorInfo.context?.itemType,\n requestId: this.getRequestId(req),\n validOptions: errorInfo.details?.validOptions,\n suggestedAction: errorInfo.details?.suggestedAction,\n retryable: errorInfo.details?.retryable,\n request: {\n method: req.method,\n path: req.path,\n headers: req.headers,\n body: req.body,\n query: req.query,\n params: req.params\n },\n suggestion: errorInfo.details?.suggestedAction || 'Check request parameters, authentication, and data validation',\n errorInfo\n });\n }\n\n // Send structured error response\n res.status(statusCode).json({\n success: false,\n error: errorInfo\n });\n };\n}\n\n/**\n * Export convenience function\n */\nexport function createErrorHandler(options?: ErrorHandlerOptions) {\n const handler = new FjellErrorHandler(options);\n return handler.handle;\n}\n\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["technical"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjell/express-router",
|
|
3
3
|
"description": "Express Router for Fjell",
|
|
4
|
-
"version": "4.4.
|
|
4
|
+
"version": "4.4.78",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"express",
|
|
7
7
|
"router",
|
|
@@ -34,17 +34,17 @@
|
|
|
34
34
|
"docs:test": "cd docs && npm run test"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@fjell/core": "^4.4.
|
|
38
|
-
"@fjell/lib": "^4.4.
|
|
39
|
-
"@fjell/logging": "^4.4.
|
|
40
|
-
"@fjell/registry": "^4.4.
|
|
37
|
+
"@fjell/core": "^4.4.73",
|
|
38
|
+
"@fjell/lib": "^4.4.82",
|
|
39
|
+
"@fjell/logging": "^4.4.65",
|
|
40
|
+
"@fjell/registry": "^4.4.81",
|
|
41
41
|
"deepmerge": "^4.3.1",
|
|
42
42
|
"express": "^5.1.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@eslint/eslintrc": "^3.3.1",
|
|
46
46
|
"@eslint/js": "^9.38.0",
|
|
47
|
-
"@fjell/common-config": "^1.1.
|
|
47
|
+
"@fjell/common-config": "^1.1.36",
|
|
48
48
|
"@tsconfig/recommended": "^1.0.10",
|
|
49
49
|
"@types/express": "^5.0.3",
|
|
50
50
|
"@types/node": "^24.9.1",
|