@emmett-community/emmett-expressjs-with-openapi 0.1.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/LICENSE +21 -0
- package/README.md +100 -0
- package/dist/chunk-5TC7YUZR.js +38 -0
- package/dist/chunk-5TC7YUZR.js.map +1 -0
- package/dist/chunk-GS7T56RP.cjs +8 -0
- package/dist/chunk-GS7T56RP.cjs.map +1 -0
- package/dist/chunk-MRSGTROW.cjs +42 -0
- package/dist/chunk-MRSGTROW.cjs.map +1 -0
- package/dist/esm-resolver-I5MW2PIQ.cjs +10 -0
- package/dist/esm-resolver-I5MW2PIQ.cjs.map +1 -0
- package/dist/esm-resolver-ZL5JTVDP.js +9 -0
- package/dist/esm-resolver-ZL5JTVDP.js.map +1 -0
- package/dist/handler-importer-4BVBIZX3.cjs +27 -0
- package/dist/handler-importer-4BVBIZX3.cjs.map +1 -0
- package/dist/handler-importer-OJGFQON5.js +26 -0
- package/dist/handler-importer-OJGFQON5.js.map +1 -0
- package/dist/index.cjs +428 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +440 -0
- package/dist/index.d.ts +440 -0
- package/dist/index.js +425 -0
- package/dist/index.js.map +1 -0
- package/dist/openapi-parser-6EGURSGG.js +73 -0
- package/dist/openapi-parser-6EGURSGG.js.map +1 -0
- package/dist/openapi-parser-KI7BS3DC.cjs +75 -0
- package/dist/openapi-parser-KI7BS3DC.cjs.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }require('./chunk-GS7T56RP.cjs');
|
|
2
|
+
|
|
3
|
+
// src/internal/openapi-parser.ts
|
|
4
|
+
var _promises = require('fs/promises');
|
|
5
|
+
var _path = require('path'); var _path2 = _interopRequireDefault(_path);
|
|
6
|
+
async function extractHandlerModules(apiSpec, handlersBasePath) {
|
|
7
|
+
const spec = typeof apiSpec === "string" ? await loadOpenApiSpec(apiSpec) : apiSpec;
|
|
8
|
+
if (!spec.paths || typeof spec.paths !== "object") {
|
|
9
|
+
throw new Error('Invalid OpenAPI specification: missing or invalid "paths" field');
|
|
10
|
+
}
|
|
11
|
+
const handlersMap = /* @__PURE__ */ new Map();
|
|
12
|
+
for (const [pathKey, pathItem] of Object.entries(spec.paths)) {
|
|
13
|
+
if (!pathItem || typeof pathItem !== "object") continue;
|
|
14
|
+
for (const [method, operation] of Object.entries(pathItem)) {
|
|
15
|
+
if (method === "parameters" || method === "servers") continue;
|
|
16
|
+
if (!operation || typeof operation !== "object") continue;
|
|
17
|
+
const handlerName = operation["x-eov-operation-handler"];
|
|
18
|
+
const operationId = operation["x-eov-operation-id"] || operation.operationId;
|
|
19
|
+
if (handlerName && typeof handlerName === "string") {
|
|
20
|
+
const absolutePath = resolveHandlerPath(
|
|
21
|
+
handlersBasePath,
|
|
22
|
+
handlerName
|
|
23
|
+
);
|
|
24
|
+
if (!handlersMap.has(handlerName)) {
|
|
25
|
+
handlersMap.set(handlerName, {
|
|
26
|
+
moduleName: handlerName,
|
|
27
|
+
relativePath: handlerName,
|
|
28
|
+
absolutePath,
|
|
29
|
+
operationIds: []
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (operationId) {
|
|
33
|
+
handlersMap.get(handlerName).operationIds.push(operationId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return Array.from(handlersMap.values());
|
|
39
|
+
}
|
|
40
|
+
async function loadOpenApiSpec(filePath) {
|
|
41
|
+
try {
|
|
42
|
+
const content = await _promises.readFile.call(void 0, filePath, "utf-8");
|
|
43
|
+
const ext = _path2.default.extname(filePath).toLowerCase();
|
|
44
|
+
if (ext === ".json") {
|
|
45
|
+
return JSON.parse(content);
|
|
46
|
+
} else if (ext === ".yaml" || ext === ".yml") {
|
|
47
|
+
const yaml = await Promise.resolve().then(() => _interopRequireWildcard(require("yaml")));
|
|
48
|
+
return yaml.parse(content);
|
|
49
|
+
} else {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Unsupported OpenAPI file format: ${ext}. Use .json, .yaml, or .yml`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (error.code === "ENOENT") {
|
|
56
|
+
throw new Error(`OpenAPI specification file not found: ${filePath}`);
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function resolveHandlerPath(basePath, relativePath) {
|
|
62
|
+
const normalized = _path2.default.normalize(relativePath);
|
|
63
|
+
const absolutePath = _path2.default.resolve(basePath, normalized);
|
|
64
|
+
const resolvedBase = _path2.default.resolve(basePath);
|
|
65
|
+
if (!absolutePath.startsWith(resolvedBase)) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`Invalid handler path: "${relativePath}" escapes base directory`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
return absolutePath;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
exports.extractHandlerModules = extractHandlerModules;
|
|
75
|
+
//# sourceMappingURL=openapi-parser-KI7BS3DC.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/axcosta/Git/pessoal/oss/emmett-community/emmett-expressjs-with-openapi/dist/openapi-parser-KI7BS3DC.cjs","../src/internal/openapi-parser.ts"],"names":[],"mappings":"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","file":"/Users/axcosta/Git/pessoal/oss/emmett-community/emmett-expressjs-with-openapi/dist/openapi-parser-KI7BS3DC.cjs","sourcesContent":[null,"/**\n * OpenAPI Parser for handler module discovery.\n *\n * INTERNAL MODULE - Not part of public API.\n *\n * Parses OpenAPI specifications to extract handler module paths from\n * x-eov-operation-handler fields, enabling automatic handler discovery\n * and registration.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { OpenAPIV3 } from 'express-openapi-validator/dist/framework/types';\n\nexport type HandlerModuleInfo = {\n moduleName: string; // e.g., \"shoppingCarts\"\n relativePath: string; // from x-eov-operation-handler\n absolutePath: string; // resolved full path\n operationIds: string[]; // operations using this handler\n};\n\n/**\n * Extract handler modules from OpenAPI specification.\n *\n * @param apiSpec - OpenAPI spec (file path or object)\n * @param handlersBasePath - Base path for handler modules\n * @returns Array of handler module information\n */\nexport async function extractHandlerModules(\n apiSpec: string | OpenAPIV3.Document,\n handlersBasePath: string,\n): Promise<HandlerModuleInfo[]> {\n // Load spec if it's a file path\n const spec =\n typeof apiSpec === 'string' ? await loadOpenApiSpec(apiSpec) : apiSpec;\n\n // Validate spec structure\n if (!spec.paths || typeof spec.paths !== 'object') {\n throw new Error('Invalid OpenAPI specification: missing or invalid \"paths\" field');\n }\n\n // Extract handler modules from spec\n const handlersMap = new Map<string, HandlerModuleInfo>();\n\n for (const [pathKey, pathItem] of Object.entries(spec.paths)) {\n if (!pathItem || typeof pathItem !== 'object') continue;\n\n for (const [method, operation] of Object.entries(pathItem)) {\n if (method === 'parameters' || method === 'servers') continue;\n if (!operation || typeof operation !== 'object') continue;\n\n const handlerName = (operation as any)['x-eov-operation-handler'];\n const operationId =\n (operation as any)['x-eov-operation-id'] ||\n (operation as any).operationId;\n\n if (handlerName && typeof handlerName === 'string') {\n const absolutePath = resolveHandlerPath(\n handlersBasePath,\n handlerName,\n );\n\n if (!handlersMap.has(handlerName)) {\n handlersMap.set(handlerName, {\n moduleName: handlerName,\n relativePath: handlerName,\n absolutePath,\n operationIds: [],\n });\n }\n\n if (operationId) {\n handlersMap.get(handlerName)!.operationIds.push(operationId);\n }\n }\n }\n }\n\n return Array.from(handlersMap.values());\n}\n\n/**\n * Load OpenAPI specification from file.\n */\nasync function loadOpenApiSpec(\n filePath: string,\n): Promise<OpenAPIV3.Document> {\n try {\n const content = await readFile(filePath, 'utf-8');\n const ext = path.extname(filePath).toLowerCase();\n\n if (ext === '.json') {\n return JSON.parse(content);\n } else if (ext === '.yaml' || ext === '.yml') {\n // Dynamic import to avoid bundling yaml if not needed\n const yaml = await import('yaml');\n return yaml.parse(content);\n } else {\n throw new Error(\n `Unsupported OpenAPI file format: ${ext}. Use .json, .yaml, or .yml`,\n );\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new Error(`OpenAPI specification file not found: ${filePath}`);\n }\n throw error;\n }\n}\n\n/**\n * Resolve handler module path, preventing path traversal attacks.\n */\nfunction resolveHandlerPath(\n basePath: string,\n relativePath: string,\n): string {\n // Normalize to prevent path traversal\n const normalized = path.normalize(relativePath);\n\n // Resolve absolute path\n const absolutePath = path.resolve(basePath, normalized);\n\n // Ensure path is within basePath (no escape via ../)\n const resolvedBase = path.resolve(basePath);\n if (!absolutePath.startsWith(resolvedBase)) {\n throw new Error(\n `Invalid handler path: \"${relativePath}\" escapes base directory`,\n );\n }\n\n return absolutePath;\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@emmett-community/emmett-expressjs-with-openapi",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Emmett - Event Sourcing development made simple",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsup",
|
|
9
|
+
"build:ts": "tsc",
|
|
10
|
+
"build:ts:watch": "tsc -b --watch",
|
|
11
|
+
"test": "run-s test:unit test:int test:e2e",
|
|
12
|
+
"test:unit": "glob -c \"node --import tsx --import tsx/cjs --test\" \"test/unit/**/*.unit.spec.ts\"",
|
|
13
|
+
"test:int": "glob -c \"node --import tsx --import tsx/cjs --test\" \"test/integration/**/*.int.spec.ts\"",
|
|
14
|
+
"test:e2e": "glob -c \"node --import tsx --import tsx/cjs --test\" \"test/e2e/**/*.e2e.spec.ts\"",
|
|
15
|
+
"test:watch": "node --import tsx --import tsx/cjs --test --watch",
|
|
16
|
+
"test:unit:watch": "glob -c \"node --import tsx --import tsx/cjs --test --watch\" \"test/unit/**/*.unit.spec.ts\"",
|
|
17
|
+
"test:int:watch": "glob -c \"node --import tsx --import tsx/cjs --test --watch\" \"test/integration/**/*.int.spec.ts\"",
|
|
18
|
+
"test:e2e:watch": "glob -c \"node --import tsx --import tsx/cjs --test --watch\" \"test/e2e/**/*.e2e.spec.ts\""
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/emmett-community/emmett-expressjs-with-openapi.git"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"Event Sourcing"
|
|
26
|
+
],
|
|
27
|
+
"author": "Andre X Costa",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/emmett-community/emmett-expressjs-with-openapi/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://event-driven-io.github.io/emmett/",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"import": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"default": "./dist/index.js"
|
|
37
|
+
},
|
|
38
|
+
"require": {
|
|
39
|
+
"types": "./dist/index.d.cts",
|
|
40
|
+
"default": "./dist/index.cjs"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"main": "./dist/index.cjs",
|
|
45
|
+
"module": "./dist/index.js",
|
|
46
|
+
"types": "./dist/index.d.ts",
|
|
47
|
+
"files": [
|
|
48
|
+
"dist"
|
|
49
|
+
],
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@event-driven-io/emmett": "0.39.1",
|
|
52
|
+
"@types/express": "^4.17.21",
|
|
53
|
+
"@types/supertest": "^6.0.2",
|
|
54
|
+
"express": "^4.19.2",
|
|
55
|
+
"express-async-errors": "^3.1.1",
|
|
56
|
+
"express-openapi-validator": "^5.3.7",
|
|
57
|
+
"http-problem-details": "^0.1.5",
|
|
58
|
+
"supertest": "^7.0.0"
|
|
59
|
+
},
|
|
60
|
+
"peerDependenciesMeta": {
|
|
61
|
+
"express-openapi-validator": {
|
|
62
|
+
"optional": true
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@types/express": "^4.17.21",
|
|
67
|
+
"@types/supertest": "^6.0.2",
|
|
68
|
+
"express": "^4.21.2",
|
|
69
|
+
"express-async-errors": "^3.1.1",
|
|
70
|
+
"express-openapi-validator": "^5.6.0",
|
|
71
|
+
"glob": "^11.0.0",
|
|
72
|
+
"http-problem-details": "^0.1.7",
|
|
73
|
+
"npm-run-all2": "^6.2.2",
|
|
74
|
+
"supertest": "^7.0.0",
|
|
75
|
+
"tsup": "^8.2.4",
|
|
76
|
+
"tsx": "^4.17.0",
|
|
77
|
+
"typescript": "^5.5.4",
|
|
78
|
+
"yaml": "^2.8.1"
|
|
79
|
+
},
|
|
80
|
+
"publishConfig": {"access": "public"}
|
|
81
|
+
}
|