@rexeus/typeweaver 0.0.2 → 0.0.4
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/README.md +403 -70
- package/dist/LICENSE +202 -0
- package/dist/NOTICE +4 -0
- package/dist/{cli-C3cTfNLj.js → cli-Cz6q9I7F.js} +935 -1001
- package/dist/{flow--vV0j3Y-.js → flow-q2wMXrDa.js} +8 -8
- package/dist/{glimmer-B-ODUU1A.js → glimmer-wgjvri6H.js} +34 -34
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8 -6
- package/dist/{markdown-BsYj_q7V.js → markdown-Nz6Lc3gB.js} +6 -6
- package/dist/{postcss-DdgOJBTx.js → postcss-yEOijaXJ.js} +42 -42
- package/dist/run-cli-with-tsx.js +8 -6
- package/dist/templates/Index.ejs +8 -0
- package/dist/{typescript-C4gnKzhB.js → typescript-Cv79a1Qz.js} +2 -2
- package/dist/{yaml-B0tq6Ttj.js → yaml-DT3qlFoE.js} +2 -2
- package/package.json +33 -20
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
2
|
import path__default, { dirname } from 'path';
|
|
3
|
+
import url, { fileURLToPath, pathToFileURL, URL as URL$1 } from 'url';
|
|
4
|
+
import { Command } from 'commander';
|
|
3
5
|
import fs, { realpathSync, statSync } from 'fs';
|
|
4
|
-
import {
|
|
6
|
+
import { PluginRegistry, PluginContextBuilder } from '@rexeus/typeweaver-gen';
|
|
7
|
+
import TypesPlugin from '@rexeus/typeweaver-types';
|
|
8
|
+
import { render } from 'ejs';
|
|
5
9
|
import { createRequire, builtinModules } from 'module';
|
|
6
|
-
import url, { fileURLToPath, pathToFileURL, URL as URL$1 } from 'url';
|
|
7
10
|
import process3 from 'process';
|
|
8
11
|
import os from 'os';
|
|
9
12
|
import tty from 'tty';
|
|
@@ -11,202 +14,140 @@ import fs$1 from 'fs/promises';
|
|
|
11
14
|
import assert2 from 'assert';
|
|
12
15
|
import v8 from 'v8';
|
|
13
16
|
import { format, inspect } from 'util';
|
|
14
|
-
import {
|
|
15
|
-
import
|
|
16
|
-
import { Command } from 'commander';
|
|
17
|
+
import { HttpMethod, HttpStatusCode, HttpResponseDefinition, HttpOperationDefinition, HttpStatusCodeNameMap } from '@rexeus/typeweaver-core';
|
|
18
|
+
import { z } from 'zod/v4';
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
var version = "0.0.4";
|
|
21
|
+
var packageJson = {
|
|
22
|
+
version: version};
|
|
23
|
+
|
|
24
|
+
class IndexFileGenerator {
|
|
25
|
+
constructor(templateDir) {
|
|
26
|
+
this.templateDir = templateDir;
|
|
27
|
+
}
|
|
28
|
+
generate(context) {
|
|
29
|
+
const templateFilePath = path__default.join(this.templateDir, "Index.ejs");
|
|
30
|
+
const template = fs.readFileSync(templateFilePath, "utf8");
|
|
31
|
+
const indexPaths = /* @__PURE__ */ new Set();
|
|
32
|
+
for (const generatedFile of context.getGeneratedFiles()) {
|
|
33
|
+
indexPaths.add(`./${generatedFile.replace(/\.ts$/, "")}`);
|
|
34
|
+
}
|
|
35
|
+
const content = render(template, {
|
|
36
|
+
indexPaths: Array.from(indexPaths).sort()
|
|
37
|
+
});
|
|
38
|
+
fs.writeFileSync(path__default.join(context.outputDir, "index.ts"), content);
|
|
22
39
|
}
|
|
23
40
|
}
|
|
24
41
|
|
|
25
|
-
class
|
|
26
|
-
constructor(
|
|
27
|
-
super(
|
|
28
|
-
this.
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
31
|
-
this.explanation = explanation;
|
|
42
|
+
class PluginLoadingFailure extends Error {
|
|
43
|
+
constructor(pluginName, attempts) {
|
|
44
|
+
super(`Failed to load plugin '${pluginName}'`);
|
|
45
|
+
this.pluginName = pluginName;
|
|
46
|
+
this.attempts = attempts;
|
|
47
|
+
Object.setPrototypeOf(this, PluginLoadingFailure.prototype);
|
|
32
48
|
}
|
|
33
49
|
}
|
|
34
50
|
|
|
35
|
-
class
|
|
36
|
-
constructor(
|
|
37
|
-
this.
|
|
51
|
+
class PluginLoader {
|
|
52
|
+
constructor(registry, requiredPlugins, strategies = ["npm", "local"]) {
|
|
53
|
+
this.registry = registry;
|
|
54
|
+
this.requiredPlugins = requiredPlugins;
|
|
55
|
+
this.strategies = strategies;
|
|
38
56
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
sharedResponseResources: []
|
|
46
|
-
};
|
|
47
|
-
const sharedDefinitions = contents.find(
|
|
48
|
-
(content) => content.name === "shared"
|
|
49
|
-
);
|
|
50
|
-
if (sharedDefinitions) {
|
|
51
|
-
if (!sharedDefinitions.isDirectory()) {
|
|
52
|
-
throw new InvalidSharedDirError("'shared' is a file, not a directory");
|
|
53
|
-
}
|
|
54
|
-
result.sharedResponseResources = await this.getSharedResponseResources();
|
|
55
|
-
console.info(
|
|
56
|
-
`Found '${result.sharedResponseResources.length}' shared responses`
|
|
57
|
-
);
|
|
58
|
-
} else {
|
|
59
|
-
console.info("No 'shared' directory found");
|
|
57
|
+
/**
|
|
58
|
+
* Load all plugins from configuration
|
|
59
|
+
*/
|
|
60
|
+
async loadPlugins(config) {
|
|
61
|
+
for (const requiredPlugin of this.requiredPlugins) {
|
|
62
|
+
this.registry.register(requiredPlugin);
|
|
60
63
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
console.info(`Skipping '${content.name}' as it is not a directory`);
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
if (content.name === "shared") {
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
const entityName = content.name;
|
|
70
|
-
const entitySourceDir = path__default.join(this.config.sourceDir, entityName);
|
|
71
|
-
const operationResources = await this.getEntityOperationResources(
|
|
72
|
-
entitySourceDir,
|
|
73
|
-
entityName
|
|
74
|
-
);
|
|
75
|
-
result.entityResources[entityName] = operationResources;
|
|
76
|
-
console.info(
|
|
77
|
-
`Found '${operationResources.length}' operation definitions for entity '${entityName}'`
|
|
78
|
-
);
|
|
64
|
+
if (!config?.plugins) {
|
|
65
|
+
return;
|
|
79
66
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
for (const content of sharedContents) {
|
|
88
|
-
if (!content.isFile()) {
|
|
89
|
-
console.info(`Skipping '${content.name}' as it is not a file`);
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
const sourceFileName = content.name;
|
|
93
|
-
const sourceFile = path__default.join(this.config.sharedSourceDir, sourceFileName);
|
|
94
|
-
const definition = await import(sourceFile);
|
|
95
|
-
if (!definition.default) {
|
|
96
|
-
console.info(
|
|
97
|
-
`Skipping '${sourceFile}' as it does not have a default export`
|
|
98
|
-
);
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
if (!(definition.default instanceof HttpResponseDefinition)) {
|
|
102
|
-
console.info(
|
|
103
|
-
`Skipping '${sourceFile}' as it is not an instance of HttpResponseDefinition`
|
|
104
|
-
);
|
|
105
|
-
continue;
|
|
67
|
+
const successful = [];
|
|
68
|
+
for (const plugin of config.plugins) {
|
|
69
|
+
let result;
|
|
70
|
+
if (typeof plugin === "string") {
|
|
71
|
+
result = await this.loadPlugin(plugin);
|
|
72
|
+
} else {
|
|
73
|
+
result = await this.loadPlugin(plugin[0]);
|
|
106
74
|
}
|
|
107
|
-
if (
|
|
108
|
-
throw new
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
sourceFile,
|
|
112
|
-
"'isShared' property is not set to 'true'"
|
|
75
|
+
if (result.success === false) {
|
|
76
|
+
throw new PluginLoadingFailure(
|
|
77
|
+
result.error.pluginName,
|
|
78
|
+
result.error.attempts
|
|
113
79
|
);
|
|
114
80
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const outputFile = path__default.join(outputDir, outputFileName);
|
|
118
|
-
sharedResponseResources.push({
|
|
119
|
-
...definition.default,
|
|
120
|
-
isShared: true,
|
|
121
|
-
sourceDir: this.config.sharedSourceDir,
|
|
122
|
-
sourceFile,
|
|
123
|
-
sourceFileName,
|
|
124
|
-
outputFile,
|
|
125
|
-
outputFileName,
|
|
126
|
-
outputDir
|
|
127
|
-
});
|
|
81
|
+
successful.push(result.value);
|
|
82
|
+
this.registry.register(result.value.plugin);
|
|
128
83
|
}
|
|
129
|
-
|
|
84
|
+
this.reportSuccessfulLoads(successful);
|
|
130
85
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Load a plugin from a string identifier
|
|
88
|
+
*/
|
|
89
|
+
async loadPlugin(pluginName) {
|
|
90
|
+
const possiblePaths = this.generatePluginPaths(pluginName);
|
|
91
|
+
const attempts = [];
|
|
92
|
+
for (const possiblePath of possiblePaths) {
|
|
93
|
+
try {
|
|
94
|
+
const pluginPackage = await import(possiblePath);
|
|
95
|
+
if (pluginPackage.default) {
|
|
96
|
+
return {
|
|
97
|
+
success: true,
|
|
98
|
+
value: {
|
|
99
|
+
plugin: new pluginPackage.default(),
|
|
100
|
+
source: possiblePath
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
attempts.push({
|
|
105
|
+
path: possiblePath,
|
|
106
|
+
error: "No default export found"
|
|
107
|
+
});
|
|
108
|
+
} catch (error) {
|
|
109
|
+
attempts.push({
|
|
110
|
+
path: possiblePath,
|
|
111
|
+
error: error instanceof Error ? error.message : String(error)
|
|
112
|
+
});
|
|
149
113
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
success: false,
|
|
117
|
+
error: {
|
|
118
|
+
pluginName,
|
|
119
|
+
attempts
|
|
155
120
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const outputClientFileName = `${operationId}Client.ts`;
|
|
173
|
-
const outputClientFile = path__default.join(outputDir, outputClientFileName);
|
|
174
|
-
const operationResource = {
|
|
175
|
-
sourceDir,
|
|
176
|
-
sourceFile,
|
|
177
|
-
sourceFileName,
|
|
178
|
-
definition: {
|
|
179
|
-
...definition.default,
|
|
180
|
-
responses: []
|
|
181
|
-
},
|
|
182
|
-
outputDir,
|
|
183
|
-
entityName,
|
|
184
|
-
outputRequestFile,
|
|
185
|
-
outputResponseFile,
|
|
186
|
-
outputResponseValidationFile,
|
|
187
|
-
outputRequestValidationFile,
|
|
188
|
-
outputRequestFileName,
|
|
189
|
-
outputRequestValidationFileName,
|
|
190
|
-
outputResponseFileName,
|
|
191
|
-
outputResponseValidationFileName,
|
|
192
|
-
outputClientFile,
|
|
193
|
-
outputClientFileName
|
|
194
|
-
};
|
|
195
|
-
if (!definition.default.responses) {
|
|
196
|
-
throw new Error(
|
|
197
|
-
`Operation '${operationId}' does not have any responses`
|
|
198
|
-
);
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Generate possible plugin paths based on configured strategies
|
|
125
|
+
*/
|
|
126
|
+
generatePluginPaths(pluginName) {
|
|
127
|
+
const paths = [];
|
|
128
|
+
for (const strategy of this.strategies) {
|
|
129
|
+
switch (strategy) {
|
|
130
|
+
case "npm":
|
|
131
|
+
paths.push(`@rexeus/typeweaver-${pluginName}`);
|
|
132
|
+
paths.push(`@rexeus/${pluginName}`);
|
|
133
|
+
break;
|
|
134
|
+
case "local":
|
|
135
|
+
paths.push(pluginName);
|
|
136
|
+
break;
|
|
199
137
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
138
|
+
}
|
|
139
|
+
return paths;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Report successful plugin loads
|
|
143
|
+
*/
|
|
144
|
+
reportSuccessfulLoads(successful) {
|
|
145
|
+
if (successful.length > 0) {
|
|
146
|
+
console.info(`Successfully loaded ${successful.length} plugin(s):`);
|
|
147
|
+
for (const result of successful) {
|
|
148
|
+
console.info(` - ${result.plugin.name} (from ${result.source})`);
|
|
206
149
|
}
|
|
207
|
-
definitions.push(operationResource);
|
|
208
150
|
}
|
|
209
|
-
return definitions;
|
|
210
151
|
}
|
|
211
152
|
}
|
|
212
153
|
|
|
@@ -219,7 +160,7 @@ var public_exports$1 = {};
|
|
|
219
160
|
__export$1(public_exports$1, {
|
|
220
161
|
builders: () => builders,
|
|
221
162
|
printer: () => printer,
|
|
222
|
-
utils: () => utils
|
|
163
|
+
utils: () => utils
|
|
223
164
|
});
|
|
224
165
|
var DOC_TYPE_STRING$1 = "string";
|
|
225
166
|
var DOC_TYPE_ARRAY$1 = "array";
|
|
@@ -1326,7 +1267,7 @@ var builders = {
|
|
|
1326
1267
|
concat: (parts) => parts
|
|
1327
1268
|
};
|
|
1328
1269
|
var printer = { printDocToString: printDocToString$1 };
|
|
1329
|
-
var utils
|
|
1270
|
+
var utils = {
|
|
1330
1271
|
willBreak,
|
|
1331
1272
|
traverseDoc: traverse_doc_default$1,
|
|
1332
1273
|
findInDoc,
|
|
@@ -1343,7 +1284,7 @@ var doc = /*#__PURE__*/Object.freeze({
|
|
|
1343
1284
|
builders: builders,
|
|
1344
1285
|
default: public_default,
|
|
1345
1286
|
printer: printer,
|
|
1346
|
-
utils: utils
|
|
1287
|
+
utils: utils
|
|
1347
1288
|
});
|
|
1348
1289
|
|
|
1349
1290
|
const require = createRequire(import.meta.url);
|
|
@@ -22452,11 +22393,11 @@ var { parsers, printers } = createParsersAndPrinters([
|
|
|
22452
22393
|
printers: ["estree", "estree-json"]
|
|
22453
22394
|
},
|
|
22454
22395
|
{
|
|
22455
|
-
importPlugin: () => import('./flow
|
|
22396
|
+
importPlugin: () => import('./flow-q2wMXrDa.js'),
|
|
22456
22397
|
parsers: ["flow"]
|
|
22457
22398
|
},
|
|
22458
22399
|
{
|
|
22459
|
-
importPlugin: () => import('./glimmer-
|
|
22400
|
+
importPlugin: () => import('./glimmer-wgjvri6H.js'),
|
|
22460
22401
|
parsers: ["glimmer"],
|
|
22461
22402
|
printers: ["glimmer"]
|
|
22462
22403
|
},
|
|
@@ -22471,7 +22412,7 @@ var { parsers, printers } = createParsersAndPrinters([
|
|
|
22471
22412
|
printers: ["html"]
|
|
22472
22413
|
},
|
|
22473
22414
|
{
|
|
22474
|
-
importPlugin: () => import('./markdown-
|
|
22415
|
+
importPlugin: () => import('./markdown-Nz6Lc3gB.js'),
|
|
22475
22416
|
parsers: ["markdown", "mdx", "remark"],
|
|
22476
22417
|
printers: ["mdast"]
|
|
22477
22418
|
},
|
|
@@ -22480,16 +22421,16 @@ var { parsers, printers } = createParsersAndPrinters([
|
|
|
22480
22421
|
parsers: ["meriyah"]
|
|
22481
22422
|
},
|
|
22482
22423
|
{
|
|
22483
|
-
importPlugin: () => import('./postcss-
|
|
22424
|
+
importPlugin: () => import('./postcss-yEOijaXJ.js'),
|
|
22484
22425
|
parsers: ["css", "less", "scss"],
|
|
22485
22426
|
printers: ["postcss"]
|
|
22486
22427
|
},
|
|
22487
22428
|
{
|
|
22488
|
-
importPlugin: () => import('./typescript-
|
|
22429
|
+
importPlugin: () => import('./typescript-Cv79a1Qz.js'),
|
|
22489
22430
|
parsers: ["typescript"]
|
|
22490
22431
|
},
|
|
22491
22432
|
{
|
|
22492
|
-
importPlugin: () => import('./yaml-
|
|
22433
|
+
importPlugin: () => import('./yaml-DT3qlFoE.js'),
|
|
22493
22434
|
parsers: ["yaml"],
|
|
22494
22435
|
printers: ["yaml"]
|
|
22495
22436
|
}
|
|
@@ -22809,7 +22750,6 @@ var debugApis = {
|
|
|
22809
22750
|
printDocToString: withPlugins(printDocToString2),
|
|
22810
22751
|
mockable: mockable_default
|
|
22811
22752
|
};
|
|
22812
|
-
var src_default = index_exports;
|
|
22813
22753
|
|
|
22814
22754
|
class Prettier {
|
|
22815
22755
|
constructor(outputDir) {
|
|
@@ -22824,7 +22764,7 @@ class Prettier {
|
|
|
22824
22764
|
if (content.isFile()) {
|
|
22825
22765
|
const filePath = path__default.join(targetDir, content.name);
|
|
22826
22766
|
const unformatted = fs.readFileSync(filePath, "utf8");
|
|
22827
|
-
const formatted = await
|
|
22767
|
+
const formatted = await format2(unformatted, {
|
|
22828
22768
|
parser: "typescript"
|
|
22829
22769
|
});
|
|
22830
22770
|
fs.writeFileSync(filePath, formatted);
|
|
@@ -22835,845 +22775,823 @@ class Prettier {
|
|
|
22835
22775
|
}
|
|
22836
22776
|
}
|
|
22837
22777
|
|
|
22838
|
-
|
|
22839
|
-
|
|
22778
|
+
class DuplicateOperationIdError extends Error {
|
|
22779
|
+
constructor(operationId, firstFile, duplicateFile) {
|
|
22780
|
+
super(
|
|
22781
|
+
`Duplicate operation ID '${operationId}' found.
|
|
22782
|
+
First defined in: \`${firstFile}\`
|
|
22783
|
+
Duplicate found in: \`${duplicateFile}\``
|
|
22784
|
+
);
|
|
22785
|
+
this.operationId = operationId;
|
|
22786
|
+
this.firstFile = firstFile;
|
|
22787
|
+
this.duplicateFile = duplicateFile;
|
|
22788
|
+
}
|
|
22840
22789
|
}
|
|
22841
22790
|
|
|
22842
|
-
|
|
22791
|
+
class DuplicateResponseNameError extends Error {
|
|
22792
|
+
constructor(responseName, firstFile, duplicateFile) {
|
|
22793
|
+
super(
|
|
22794
|
+
`Duplicate response name '${responseName}' found.
|
|
22795
|
+
First defined in: \`${firstFile}\`
|
|
22796
|
+
Duplicate found in: \`${duplicateFile}\``
|
|
22797
|
+
);
|
|
22798
|
+
this.responseName = responseName;
|
|
22799
|
+
this.firstFile = firstFile;
|
|
22800
|
+
this.duplicateFile = duplicateFile;
|
|
22801
|
+
}
|
|
22802
|
+
}
|
|
22843
22803
|
|
|
22844
|
-
|
|
22845
|
-
|
|
22846
|
-
|
|
22804
|
+
class DuplicateRouteError extends Error {
|
|
22805
|
+
constructor(path, method, firstOperationId, firstFile, duplicateOperationId, duplicateFile) {
|
|
22806
|
+
super(
|
|
22807
|
+
`Duplicate route '${method} ${path}' found.
|
|
22808
|
+
First defined by operation '${firstOperationId}' in: \`${firstFile}\`
|
|
22809
|
+
Duplicate defined by operation '${duplicateOperationId}' in: \`${duplicateFile}\``
|
|
22810
|
+
);
|
|
22811
|
+
this.path = path;
|
|
22812
|
+
this.method = method;
|
|
22813
|
+
this.firstOperationId = firstOperationId;
|
|
22814
|
+
this.firstFile = firstFile;
|
|
22815
|
+
this.duplicateOperationId = duplicateOperationId;
|
|
22816
|
+
this.duplicateFile = duplicateFile;
|
|
22817
|
+
}
|
|
22818
|
+
}
|
|
22847
22819
|
|
|
22848
|
-
|
|
22849
|
-
|
|
22850
|
-
|
|
22851
|
-
|
|
22852
|
-
|
|
22853
|
-
|
|
22854
|
-
|
|
22855
|
-
|
|
22856
|
-
|
|
22857
|
-
|
|
22858
|
-
|
|
22859
|
-
|
|
22860
|
-
|
|
22861
|
-
|
|
22862
|
-
|
|
22863
|
-
|
|
22864
|
-
|
|
22865
|
-
|
|
22866
|
-
|
|
22867
|
-
|
|
22868
|
-
|
|
22869
|
-
|
|
22870
|
-
|
|
22871
|
-
|
|
22872
|
-
|
|
22873
|
-
|
|
22874
|
-
|
|
22875
|
-
|
|
22876
|
-
|
|
22877
|
-
|
|
22878
|
-
|
|
22879
|
-
|
|
22880
|
-
|
|
22881
|
-
|
|
22882
|
-
|
|
22883
|
-
|
|
22884
|
-
|
|
22885
|
-
|
|
22886
|
-
|
|
22887
|
-
|
|
22888
|
-
|
|
22889
|
-
|
|
22890
|
-
|
|
22891
|
-
|
|
22892
|
-
|
|
22893
|
-
|
|
22894
|
-
|
|
22895
|
-
|
|
22896
|
-
|
|
22897
|
-
|
|
22898
|
-
|
|
22899
|
-
|
|
22900
|
-
|
|
22901
|
-
|
|
22902
|
-
|
|
22903
|
-
|
|
22904
|
-
|
|
22905
|
-
|
|
22906
|
-
|
|
22907
|
-
|
|
22908
|
-
|
|
22909
|
-
|
|
22910
|
-
|
|
22911
|
-
|
|
22912
|
-
|
|
22913
|
-
|
|
22914
|
-
|
|
22915
|
-
|
|
22916
|
-
|
|
22917
|
-
|
|
22918
|
-
|
|
22919
|
-
if (to !== null && to !== void 0) {
|
|
22920
|
-
for (var i = 0; i < list.length; i++) {
|
|
22921
|
-
var p = list[i];
|
|
22922
|
-
if (typeof from[p] != "undefined") {
|
|
22923
|
-
if (!hasOwn(from, p)) {
|
|
22924
|
-
continue;
|
|
22925
|
-
}
|
|
22926
|
-
if (p === "__proto__" || p === "constructor") {
|
|
22927
|
-
continue;
|
|
22928
|
-
}
|
|
22929
|
-
to[p] = from[p];
|
|
22930
|
-
}
|
|
22931
|
-
}
|
|
22932
|
-
}
|
|
22933
|
-
return to;
|
|
22934
|
-
};
|
|
22935
|
-
exports.cache = {
|
|
22936
|
-
_data: {},
|
|
22937
|
-
set: function(key, val) {
|
|
22938
|
-
this._data[key] = val;
|
|
22939
|
-
},
|
|
22940
|
-
get: function(key) {
|
|
22941
|
-
return this._data[key];
|
|
22942
|
-
},
|
|
22943
|
-
remove: function(key) {
|
|
22944
|
-
delete this._data[key];
|
|
22945
|
-
},
|
|
22946
|
-
reset: function() {
|
|
22947
|
-
this._data = {};
|
|
22948
|
-
}
|
|
22949
|
-
};
|
|
22950
|
-
exports.hyphenToCamel = function(str) {
|
|
22951
|
-
return str.replace(/-[a-z]/g, function(match) {
|
|
22952
|
-
return match[1].toUpperCase();
|
|
22953
|
-
});
|
|
22954
|
-
};
|
|
22955
|
-
exports.createNullProtoObjWherePossible = function() {
|
|
22956
|
-
if (typeof Object.create == "function") {
|
|
22957
|
-
return function() {
|
|
22958
|
-
return /* @__PURE__ */ Object.create(null);
|
|
22959
|
-
};
|
|
22960
|
-
}
|
|
22961
|
-
if (!({ __proto__: null } instanceof Object)) {
|
|
22962
|
-
return function() {
|
|
22963
|
-
return { __proto__: null };
|
|
22964
|
-
};
|
|
22965
|
-
}
|
|
22966
|
-
return function() {
|
|
22967
|
-
return {};
|
|
22968
|
-
};
|
|
22969
|
-
}();
|
|
22970
|
-
exports.hasOwnOnlyObject = function(obj) {
|
|
22971
|
-
var o = exports.createNullProtoObjWherePossible();
|
|
22972
|
-
for (var p in obj) {
|
|
22973
|
-
if (hasOwn(obj, p)) {
|
|
22974
|
-
o[p] = obj[p];
|
|
22975
|
-
}
|
|
22976
|
-
}
|
|
22977
|
-
return o;
|
|
22978
|
-
};
|
|
22979
|
-
} (utils));
|
|
22980
|
-
return utils;
|
|
22820
|
+
class DefinitionRegistry {
|
|
22821
|
+
operationIds = /* @__PURE__ */ new Map();
|
|
22822
|
+
responseNames = /* @__PURE__ */ new Map();
|
|
22823
|
+
routes = /* @__PURE__ */ new Map();
|
|
22824
|
+
registerOperation(operation, sourceFile) {
|
|
22825
|
+
const existingFile = this.operationIds.get(operation.operationId);
|
|
22826
|
+
if (existingFile) {
|
|
22827
|
+
throw new DuplicateOperationIdError(
|
|
22828
|
+
operation.operationId,
|
|
22829
|
+
existingFile,
|
|
22830
|
+
sourceFile
|
|
22831
|
+
);
|
|
22832
|
+
}
|
|
22833
|
+
this.operationIds.set(operation.operationId, sourceFile);
|
|
22834
|
+
const normalizedPath = this.normalizePath(operation.path);
|
|
22835
|
+
const routeKey = `${operation.method} ${normalizedPath}`;
|
|
22836
|
+
const existingRoute = this.routes.get(routeKey);
|
|
22837
|
+
if (existingRoute) {
|
|
22838
|
+
throw new DuplicateRouteError(
|
|
22839
|
+
operation.path,
|
|
22840
|
+
operation.method,
|
|
22841
|
+
existingRoute.operationId,
|
|
22842
|
+
existingRoute.file,
|
|
22843
|
+
operation.operationId,
|
|
22844
|
+
sourceFile
|
|
22845
|
+
);
|
|
22846
|
+
}
|
|
22847
|
+
this.routes.set(routeKey, {
|
|
22848
|
+
operationId: operation.operationId,
|
|
22849
|
+
file: sourceFile
|
|
22850
|
+
});
|
|
22851
|
+
}
|
|
22852
|
+
registerResponse(response, sourceFile) {
|
|
22853
|
+
const existingFile = this.responseNames.get(response.name);
|
|
22854
|
+
if (existingFile) {
|
|
22855
|
+
throw new DuplicateResponseNameError(
|
|
22856
|
+
response.name,
|
|
22857
|
+
existingFile,
|
|
22858
|
+
sourceFile
|
|
22859
|
+
);
|
|
22860
|
+
}
|
|
22861
|
+
this.responseNames.set(response.name, sourceFile);
|
|
22862
|
+
}
|
|
22863
|
+
hasOperationId(operationId) {
|
|
22864
|
+
return this.operationIds.has(operationId);
|
|
22865
|
+
}
|
|
22866
|
+
hasResponseName(name) {
|
|
22867
|
+
return this.responseNames.has(name);
|
|
22868
|
+
}
|
|
22869
|
+
hasRoute(method, path) {
|
|
22870
|
+
const normalizedPath = this.normalizePath(path);
|
|
22871
|
+
const routeKey = `${method} ${normalizedPath}`;
|
|
22872
|
+
return this.routes.has(routeKey);
|
|
22873
|
+
}
|
|
22874
|
+
getOperationFile(operationId) {
|
|
22875
|
+
return this.operationIds.get(operationId);
|
|
22876
|
+
}
|
|
22877
|
+
getResponseFile(name) {
|
|
22878
|
+
return this.responseNames.get(name);
|
|
22879
|
+
}
|
|
22880
|
+
getRouteInfo(method, path) {
|
|
22881
|
+
const normalizedPath = this.normalizePath(path);
|
|
22882
|
+
const routeKey = `${method} ${normalizedPath}`;
|
|
22883
|
+
return this.routes.get(routeKey);
|
|
22884
|
+
}
|
|
22885
|
+
normalizePath(path) {
|
|
22886
|
+
let paramIndex = 1;
|
|
22887
|
+
return path.replace(/:([a-zA-Z0-9_]+)/g, () => {
|
|
22888
|
+
return `:param${paramIndex++}`;
|
|
22889
|
+
});
|
|
22890
|
+
}
|
|
22981
22891
|
}
|
|
22982
22892
|
|
|
22983
|
-
|
|
22984
|
-
|
|
22985
|
-
|
|
22893
|
+
class EmptyResponseArrayError extends Error {
|
|
22894
|
+
constructor(operationId, file) {
|
|
22895
|
+
super(
|
|
22896
|
+
`Operation '${operationId}' has no responses defined at \`${file}\`
|
|
22897
|
+
Operations must have at least one response definition`
|
|
22898
|
+
);
|
|
22899
|
+
this.operationId = operationId;
|
|
22900
|
+
this.file = file;
|
|
22901
|
+
}
|
|
22902
|
+
}
|
|
22986
22903
|
|
|
22987
|
-
|
|
22904
|
+
class InvalidHttpMethodError extends Error {
|
|
22905
|
+
constructor(operationId, method, file) {
|
|
22906
|
+
const validMethods = Object.values(HttpMethod).join(", ");
|
|
22907
|
+
super(
|
|
22908
|
+
`Invalid HTTP method '${method}' in operation '${operationId}' at \`${file}\`
|
|
22909
|
+
Valid methods: ${validMethods}`
|
|
22910
|
+
);
|
|
22911
|
+
this.operationId = operationId;
|
|
22912
|
+
this.method = method;
|
|
22913
|
+
this.file = file;
|
|
22914
|
+
}
|
|
22915
|
+
}
|
|
22988
22916
|
|
|
22989
|
-
|
|
22990
|
-
|
|
22991
|
-
|
|
22992
|
-
|
|
22993
|
-
|
|
22994
|
-
|
|
22995
|
-
|
|
22996
|
-
|
|
22997
|
-
|
|
22998
|
-
|
|
22999
|
-
|
|
23000
|
-
|
|
23001
|
-
var path = path__default;
|
|
23002
|
-
var utils = requireUtils();
|
|
23003
|
-
var scopeOptionWarned = false;
|
|
23004
|
-
var _VERSION_STRING = require$$3.version;
|
|
23005
|
-
var _DEFAULT_OPEN_DELIMITER = "<";
|
|
23006
|
-
var _DEFAULT_CLOSE_DELIMITER = ">";
|
|
23007
|
-
var _DEFAULT_DELIMITER = "%";
|
|
23008
|
-
var _DEFAULT_LOCALS_NAME = "locals";
|
|
23009
|
-
var _NAME = "ejs";
|
|
23010
|
-
var _REGEX_STRING = "(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)";
|
|
23011
|
-
var _OPTS_PASSABLE_WITH_DATA = [
|
|
23012
|
-
"delimiter",
|
|
23013
|
-
"scope",
|
|
23014
|
-
"context",
|
|
23015
|
-
"debug",
|
|
23016
|
-
"compileDebug",
|
|
23017
|
-
"client",
|
|
23018
|
-
"_with",
|
|
23019
|
-
"rmWhitespace",
|
|
23020
|
-
"strict",
|
|
23021
|
-
"filename",
|
|
23022
|
-
"async"
|
|
23023
|
-
];
|
|
23024
|
-
var _OPTS_PASSABLE_WITH_DATA_EXPRESS = _OPTS_PASSABLE_WITH_DATA.concat("cache");
|
|
23025
|
-
var _BOM = /^\uFEFF/;
|
|
23026
|
-
var _JS_IDENTIFIER = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;
|
|
23027
|
-
exports.cache = utils.cache;
|
|
23028
|
-
exports.fileLoader = fs$1.readFileSync;
|
|
23029
|
-
exports.localsName = _DEFAULT_LOCALS_NAME;
|
|
23030
|
-
exports.promiseImpl = new Function("return this;")().Promise;
|
|
23031
|
-
exports.resolveInclude = function(name, filename, isDir) {
|
|
23032
|
-
var dirname = path.dirname;
|
|
23033
|
-
var extname = path.extname;
|
|
23034
|
-
var resolve = path.resolve;
|
|
23035
|
-
var includePath = resolve(isDir ? filename : dirname(filename), name);
|
|
23036
|
-
var ext = extname(name);
|
|
23037
|
-
if (!ext) {
|
|
23038
|
-
includePath += ".ejs";
|
|
23039
|
-
}
|
|
23040
|
-
return includePath;
|
|
23041
|
-
};
|
|
23042
|
-
function resolvePaths(name, paths) {
|
|
23043
|
-
var filePath;
|
|
23044
|
-
if (paths.some(function(v) {
|
|
23045
|
-
filePath = exports.resolveInclude(name, v, true);
|
|
23046
|
-
return fs$1.existsSync(filePath);
|
|
23047
|
-
})) {
|
|
23048
|
-
return filePath;
|
|
23049
|
-
}
|
|
23050
|
-
}
|
|
23051
|
-
function getIncludePath(path2, options) {
|
|
23052
|
-
var includePath;
|
|
23053
|
-
var filePath;
|
|
23054
|
-
var views = options.views;
|
|
23055
|
-
var match = /^[A-Za-z]+:\\|^\//.exec(path2);
|
|
23056
|
-
if (match && match.length) {
|
|
23057
|
-
path2 = path2.replace(/^\/*/, "");
|
|
23058
|
-
if (Array.isArray(options.root)) {
|
|
23059
|
-
includePath = resolvePaths(path2, options.root);
|
|
23060
|
-
} else {
|
|
23061
|
-
includePath = exports.resolveInclude(path2, options.root || "/", true);
|
|
23062
|
-
}
|
|
23063
|
-
} else {
|
|
23064
|
-
if (options.filename) {
|
|
23065
|
-
filePath = exports.resolveInclude(path2, options.filename);
|
|
23066
|
-
if (fs$1.existsSync(filePath)) {
|
|
23067
|
-
includePath = filePath;
|
|
23068
|
-
}
|
|
23069
|
-
}
|
|
23070
|
-
if (!includePath && Array.isArray(views)) {
|
|
23071
|
-
includePath = resolvePaths(path2, views);
|
|
23072
|
-
}
|
|
23073
|
-
if (!includePath && typeof options.includer !== "function") {
|
|
23074
|
-
throw new Error('Could not find the include file "' + options.escapeFunction(path2) + '"');
|
|
23075
|
-
}
|
|
23076
|
-
}
|
|
23077
|
-
return includePath;
|
|
23078
|
-
}
|
|
23079
|
-
function handleCache(options, template) {
|
|
23080
|
-
var func;
|
|
23081
|
-
var filename = options.filename;
|
|
23082
|
-
var hasTemplate = arguments.length > 1;
|
|
23083
|
-
if (options.cache) {
|
|
23084
|
-
if (!filename) {
|
|
23085
|
-
throw new Error("cache option requires a filename");
|
|
23086
|
-
}
|
|
23087
|
-
func = exports.cache.get(filename);
|
|
23088
|
-
if (func) {
|
|
23089
|
-
return func;
|
|
23090
|
-
}
|
|
23091
|
-
if (!hasTemplate) {
|
|
23092
|
-
template = fileLoader(filename).toString().replace(_BOM, "");
|
|
23093
|
-
}
|
|
23094
|
-
} else if (!hasTemplate) {
|
|
23095
|
-
if (!filename) {
|
|
23096
|
-
throw new Error("Internal EJS error: no file name or template provided");
|
|
23097
|
-
}
|
|
23098
|
-
template = fileLoader(filename).toString().replace(_BOM, "");
|
|
23099
|
-
}
|
|
23100
|
-
func = exports.compile(template, options);
|
|
23101
|
-
if (options.cache) {
|
|
23102
|
-
exports.cache.set(filename, func);
|
|
23103
|
-
}
|
|
23104
|
-
return func;
|
|
23105
|
-
}
|
|
23106
|
-
function tryHandleCache(options, data, cb) {
|
|
23107
|
-
var result;
|
|
23108
|
-
if (!cb) {
|
|
23109
|
-
if (typeof exports.promiseImpl == "function") {
|
|
23110
|
-
return new exports.promiseImpl(function(resolve, reject) {
|
|
23111
|
-
try {
|
|
23112
|
-
result = handleCache(options)(data);
|
|
23113
|
-
resolve(result);
|
|
23114
|
-
} catch (err) {
|
|
23115
|
-
reject(err);
|
|
23116
|
-
}
|
|
23117
|
-
});
|
|
23118
|
-
} else {
|
|
23119
|
-
throw new Error("Please provide a callback function");
|
|
23120
|
-
}
|
|
23121
|
-
} else {
|
|
23122
|
-
try {
|
|
23123
|
-
result = handleCache(options)(data);
|
|
23124
|
-
} catch (err) {
|
|
23125
|
-
return cb(err);
|
|
23126
|
-
}
|
|
23127
|
-
cb(null, result);
|
|
23128
|
-
}
|
|
23129
|
-
}
|
|
23130
|
-
function fileLoader(filePath) {
|
|
23131
|
-
return exports.fileLoader(filePath);
|
|
23132
|
-
}
|
|
23133
|
-
function includeFile(path2, options) {
|
|
23134
|
-
var opts = utils.shallowCopy(utils.createNullProtoObjWherePossible(), options);
|
|
23135
|
-
opts.filename = getIncludePath(path2, opts);
|
|
23136
|
-
if (typeof options.includer === "function") {
|
|
23137
|
-
var includerResult = options.includer(path2, opts.filename);
|
|
23138
|
-
if (includerResult) {
|
|
23139
|
-
if (includerResult.filename) {
|
|
23140
|
-
opts.filename = includerResult.filename;
|
|
23141
|
-
}
|
|
23142
|
-
if (includerResult.template) {
|
|
23143
|
-
return handleCache(opts, includerResult.template);
|
|
23144
|
-
}
|
|
23145
|
-
}
|
|
23146
|
-
}
|
|
23147
|
-
return handleCache(opts);
|
|
23148
|
-
}
|
|
23149
|
-
function rethrow(err, str, flnm, lineno, esc) {
|
|
23150
|
-
var lines = str.split("\n");
|
|
23151
|
-
var start = Math.max(lineno - 3, 0);
|
|
23152
|
-
var end = Math.min(lines.length, lineno + 3);
|
|
23153
|
-
var filename = esc(flnm);
|
|
23154
|
-
var context = lines.slice(start, end).map(function(line, i) {
|
|
23155
|
-
var curr = i + start + 1;
|
|
23156
|
-
return (curr == lineno ? " >> " : " ") + curr + "| " + line;
|
|
23157
|
-
}).join("\n");
|
|
23158
|
-
err.path = filename;
|
|
23159
|
-
err.message = (filename || "ejs") + ":" + lineno + "\n" + context + "\n\n" + err.message;
|
|
23160
|
-
throw err;
|
|
23161
|
-
}
|
|
23162
|
-
function stripSemi(str) {
|
|
23163
|
-
return str.replace(/;(\s*$)/, "$1");
|
|
23164
|
-
}
|
|
23165
|
-
exports.compile = function compile(template, opts) {
|
|
23166
|
-
var templ;
|
|
23167
|
-
if (opts && opts.scope) {
|
|
23168
|
-
if (!scopeOptionWarned) {
|
|
23169
|
-
console.warn("`scope` option is deprecated and will be removed in EJS 3");
|
|
23170
|
-
scopeOptionWarned = true;
|
|
23171
|
-
}
|
|
23172
|
-
if (!opts.context) {
|
|
23173
|
-
opts.context = opts.scope;
|
|
23174
|
-
}
|
|
23175
|
-
delete opts.scope;
|
|
23176
|
-
}
|
|
23177
|
-
templ = new Template(template, opts);
|
|
23178
|
-
return templ.compile();
|
|
23179
|
-
};
|
|
23180
|
-
exports.render = function(template, d, o) {
|
|
23181
|
-
var data = d || utils.createNullProtoObjWherePossible();
|
|
23182
|
-
var opts = o || utils.createNullProtoObjWherePossible();
|
|
23183
|
-
if (arguments.length == 2) {
|
|
23184
|
-
utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA);
|
|
23185
|
-
}
|
|
23186
|
-
return handleCache(opts, template)(data);
|
|
23187
|
-
};
|
|
23188
|
-
exports.renderFile = function() {
|
|
23189
|
-
var args = Array.prototype.slice.call(arguments);
|
|
23190
|
-
var filename = args.shift();
|
|
23191
|
-
var cb;
|
|
23192
|
-
var opts = { filename };
|
|
23193
|
-
var data;
|
|
23194
|
-
var viewOpts;
|
|
23195
|
-
if (typeof arguments[arguments.length - 1] == "function") {
|
|
23196
|
-
cb = args.pop();
|
|
23197
|
-
}
|
|
23198
|
-
if (args.length) {
|
|
23199
|
-
data = args.shift();
|
|
23200
|
-
if (args.length) {
|
|
23201
|
-
utils.shallowCopy(opts, args.pop());
|
|
23202
|
-
} else {
|
|
23203
|
-
if (data.settings) {
|
|
23204
|
-
if (data.settings.views) {
|
|
23205
|
-
opts.views = data.settings.views;
|
|
23206
|
-
}
|
|
23207
|
-
if (data.settings["view cache"]) {
|
|
23208
|
-
opts.cache = true;
|
|
23209
|
-
}
|
|
23210
|
-
viewOpts = data.settings["view options"];
|
|
23211
|
-
if (viewOpts) {
|
|
23212
|
-
utils.shallowCopy(opts, viewOpts);
|
|
23213
|
-
}
|
|
23214
|
-
}
|
|
23215
|
-
utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA_EXPRESS);
|
|
23216
|
-
}
|
|
23217
|
-
opts.filename = filename;
|
|
23218
|
-
} else {
|
|
23219
|
-
data = utils.createNullProtoObjWherePossible();
|
|
23220
|
-
}
|
|
23221
|
-
return tryHandleCache(opts, data, cb);
|
|
23222
|
-
};
|
|
23223
|
-
exports.Template = Template;
|
|
23224
|
-
exports.clearCache = function() {
|
|
23225
|
-
exports.cache.reset();
|
|
23226
|
-
};
|
|
23227
|
-
function Template(text, optsParam) {
|
|
23228
|
-
var opts = utils.hasOwnOnlyObject(optsParam);
|
|
23229
|
-
var options = utils.createNullProtoObjWherePossible();
|
|
23230
|
-
this.templateText = text;
|
|
23231
|
-
this.mode = null;
|
|
23232
|
-
this.truncate = false;
|
|
23233
|
-
this.currentLine = 1;
|
|
23234
|
-
this.source = "";
|
|
23235
|
-
options.client = opts.client || false;
|
|
23236
|
-
options.escapeFunction = opts.escape || opts.escapeFunction || utils.escapeXML;
|
|
23237
|
-
options.compileDebug = opts.compileDebug !== false;
|
|
23238
|
-
options.debug = !!opts.debug;
|
|
23239
|
-
options.filename = opts.filename;
|
|
23240
|
-
options.openDelimiter = opts.openDelimiter || exports.openDelimiter || _DEFAULT_OPEN_DELIMITER;
|
|
23241
|
-
options.closeDelimiter = opts.closeDelimiter || exports.closeDelimiter || _DEFAULT_CLOSE_DELIMITER;
|
|
23242
|
-
options.delimiter = opts.delimiter || exports.delimiter || _DEFAULT_DELIMITER;
|
|
23243
|
-
options.strict = opts.strict || false;
|
|
23244
|
-
options.context = opts.context;
|
|
23245
|
-
options.cache = opts.cache || false;
|
|
23246
|
-
options.rmWhitespace = opts.rmWhitespace;
|
|
23247
|
-
options.root = opts.root;
|
|
23248
|
-
options.includer = opts.includer;
|
|
23249
|
-
options.outputFunctionName = opts.outputFunctionName;
|
|
23250
|
-
options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME;
|
|
23251
|
-
options.views = opts.views;
|
|
23252
|
-
options.async = opts.async;
|
|
23253
|
-
options.destructuredLocals = opts.destructuredLocals;
|
|
23254
|
-
options.legacyInclude = typeof opts.legacyInclude != "undefined" ? !!opts.legacyInclude : true;
|
|
23255
|
-
if (options.strict) {
|
|
23256
|
-
options._with = false;
|
|
23257
|
-
} else {
|
|
23258
|
-
options._with = typeof opts._with != "undefined" ? opts._with : true;
|
|
23259
|
-
}
|
|
23260
|
-
this.opts = options;
|
|
23261
|
-
this.regex = this.createRegex();
|
|
23262
|
-
}
|
|
23263
|
-
Template.modes = {
|
|
23264
|
-
EVAL: "eval",
|
|
23265
|
-
ESCAPED: "escaped",
|
|
23266
|
-
RAW: "raw",
|
|
23267
|
-
COMMENT: "comment",
|
|
23268
|
-
LITERAL: "literal"
|
|
23269
|
-
};
|
|
23270
|
-
Template.prototype = {
|
|
23271
|
-
createRegex: function() {
|
|
23272
|
-
var str = _REGEX_STRING;
|
|
23273
|
-
var delim = utils.escapeRegExpChars(this.opts.delimiter);
|
|
23274
|
-
var open = utils.escapeRegExpChars(this.opts.openDelimiter);
|
|
23275
|
-
var close = utils.escapeRegExpChars(this.opts.closeDelimiter);
|
|
23276
|
-
str = str.replace(/%/g, delim).replace(/</g, open).replace(/>/g, close);
|
|
23277
|
-
return new RegExp(str);
|
|
23278
|
-
},
|
|
23279
|
-
compile: function() {
|
|
23280
|
-
var src;
|
|
23281
|
-
var fn;
|
|
23282
|
-
var opts = this.opts;
|
|
23283
|
-
var prepended = "";
|
|
23284
|
-
var appended = "";
|
|
23285
|
-
var escapeFn = opts.escapeFunction;
|
|
23286
|
-
var ctor;
|
|
23287
|
-
var sanitizedFilename = opts.filename ? JSON.stringify(opts.filename) : "undefined";
|
|
23288
|
-
if (!this.source) {
|
|
23289
|
-
this.generateSource();
|
|
23290
|
-
prepended += ' var __output = "";\n function __append(s) { if (s !== undefined && s !== null) __output += s }\n';
|
|
23291
|
-
if (opts.outputFunctionName) {
|
|
23292
|
-
if (!_JS_IDENTIFIER.test(opts.outputFunctionName)) {
|
|
23293
|
-
throw new Error("outputFunctionName is not a valid JS identifier.");
|
|
23294
|
-
}
|
|
23295
|
-
prepended += " var " + opts.outputFunctionName + " = __append;\n";
|
|
23296
|
-
}
|
|
23297
|
-
if (opts.localsName && !_JS_IDENTIFIER.test(opts.localsName)) {
|
|
23298
|
-
throw new Error("localsName is not a valid JS identifier.");
|
|
23299
|
-
}
|
|
23300
|
-
if (opts.destructuredLocals && opts.destructuredLocals.length) {
|
|
23301
|
-
var destructuring = " var __locals = (" + opts.localsName + " || {}),\n";
|
|
23302
|
-
for (var i = 0; i < opts.destructuredLocals.length; i++) {
|
|
23303
|
-
var name = opts.destructuredLocals[i];
|
|
23304
|
-
if (!_JS_IDENTIFIER.test(name)) {
|
|
23305
|
-
throw new Error("destructuredLocals[" + i + "] is not a valid JS identifier.");
|
|
23306
|
-
}
|
|
23307
|
-
if (i > 0) {
|
|
23308
|
-
destructuring += ",\n ";
|
|
23309
|
-
}
|
|
23310
|
-
destructuring += name + " = __locals." + name;
|
|
23311
|
-
}
|
|
23312
|
-
prepended += destructuring + ";\n";
|
|
23313
|
-
}
|
|
23314
|
-
if (opts._with !== false) {
|
|
23315
|
-
prepended += " with (" + opts.localsName + " || {}) {\n";
|
|
23316
|
-
appended += " }\n";
|
|
23317
|
-
}
|
|
23318
|
-
appended += " return __output;\n";
|
|
23319
|
-
this.source = prepended + this.source + appended;
|
|
23320
|
-
}
|
|
23321
|
-
if (opts.compileDebug) {
|
|
23322
|
-
src = "var __line = 1\n , __lines = " + JSON.stringify(this.templateText) + "\n , __filename = " + sanitizedFilename + ";\ntry {\n" + this.source + "} catch (e) {\n rethrow(e, __lines, __filename, __line, escapeFn);\n}\n";
|
|
23323
|
-
} else {
|
|
23324
|
-
src = this.source;
|
|
23325
|
-
}
|
|
23326
|
-
if (opts.client) {
|
|
23327
|
-
src = "escapeFn = escapeFn || " + escapeFn.toString() + ";\n" + src;
|
|
23328
|
-
if (opts.compileDebug) {
|
|
23329
|
-
src = "rethrow = rethrow || " + rethrow.toString() + ";\n" + src;
|
|
23330
|
-
}
|
|
23331
|
-
}
|
|
23332
|
-
if (opts.strict) {
|
|
23333
|
-
src = '"use strict";\n' + src;
|
|
23334
|
-
}
|
|
23335
|
-
if (opts.debug) {
|
|
23336
|
-
console.log(src);
|
|
23337
|
-
}
|
|
23338
|
-
if (opts.compileDebug && opts.filename) {
|
|
23339
|
-
src = src + "\n//# sourceURL=" + sanitizedFilename + "\n";
|
|
23340
|
-
}
|
|
23341
|
-
try {
|
|
23342
|
-
if (opts.async) {
|
|
23343
|
-
try {
|
|
23344
|
-
ctor = new Function("return (async function(){}).constructor;")();
|
|
23345
|
-
} catch (e) {
|
|
23346
|
-
if (e instanceof SyntaxError) {
|
|
23347
|
-
throw new Error("This environment does not support async/await");
|
|
23348
|
-
} else {
|
|
23349
|
-
throw e;
|
|
23350
|
-
}
|
|
23351
|
-
}
|
|
23352
|
-
} else {
|
|
23353
|
-
ctor = Function;
|
|
23354
|
-
}
|
|
23355
|
-
fn = new ctor(opts.localsName + ", escapeFn, include, rethrow", src);
|
|
23356
|
-
} catch (e) {
|
|
23357
|
-
if (e instanceof SyntaxError) {
|
|
23358
|
-
if (opts.filename) {
|
|
23359
|
-
e.message += " in " + opts.filename;
|
|
23360
|
-
}
|
|
23361
|
-
e.message += " while compiling ejs\n\n";
|
|
23362
|
-
e.message += "If the above error is not helpful, you may want to try EJS-Lint:\n";
|
|
23363
|
-
e.message += "https://github.com/RyanZim/EJS-Lint";
|
|
23364
|
-
if (!opts.async) {
|
|
23365
|
-
e.message += "\n";
|
|
23366
|
-
e.message += "Or, if you meant to create an async function, pass `async: true` as an option.";
|
|
23367
|
-
}
|
|
23368
|
-
}
|
|
23369
|
-
throw e;
|
|
23370
|
-
}
|
|
23371
|
-
var returnedFn = opts.client ? fn : function anonymous(data) {
|
|
23372
|
-
var include = function(path2, includeData) {
|
|
23373
|
-
var d = utils.shallowCopy(utils.createNullProtoObjWherePossible(), data);
|
|
23374
|
-
if (includeData) {
|
|
23375
|
-
d = utils.shallowCopy(d, includeData);
|
|
23376
|
-
}
|
|
23377
|
-
return includeFile(path2, opts)(d);
|
|
23378
|
-
};
|
|
23379
|
-
return fn.apply(
|
|
23380
|
-
opts.context,
|
|
23381
|
-
[data || utils.createNullProtoObjWherePossible(), escapeFn, include, rethrow]
|
|
23382
|
-
);
|
|
23383
|
-
};
|
|
23384
|
-
if (opts.filename && typeof Object.defineProperty === "function") {
|
|
23385
|
-
var filename = opts.filename;
|
|
23386
|
-
var basename = path.basename(filename, path.extname(filename));
|
|
23387
|
-
try {
|
|
23388
|
-
Object.defineProperty(returnedFn, "name", {
|
|
23389
|
-
value: basename,
|
|
23390
|
-
writable: false,
|
|
23391
|
-
enumerable: false,
|
|
23392
|
-
configurable: true
|
|
23393
|
-
});
|
|
23394
|
-
} catch (e) {
|
|
23395
|
-
}
|
|
23396
|
-
}
|
|
23397
|
-
return returnedFn;
|
|
23398
|
-
},
|
|
23399
|
-
generateSource: function() {
|
|
23400
|
-
var opts = this.opts;
|
|
23401
|
-
if (opts.rmWhitespace) {
|
|
23402
|
-
this.templateText = this.templateText.replace(/[\r\n]+/g, "\n").replace(/^\s+|\s+$/gm, "");
|
|
23403
|
-
}
|
|
23404
|
-
this.templateText = this.templateText.replace(/[ \t]*<%_/gm, "<%_").replace(/_%>[ \t]*/gm, "_%>");
|
|
23405
|
-
var self = this;
|
|
23406
|
-
var matches = this.parseTemplateText();
|
|
23407
|
-
var d = this.opts.delimiter;
|
|
23408
|
-
var o = this.opts.openDelimiter;
|
|
23409
|
-
var c = this.opts.closeDelimiter;
|
|
23410
|
-
if (matches && matches.length) {
|
|
23411
|
-
matches.forEach(function(line, index) {
|
|
23412
|
-
var closing;
|
|
23413
|
-
if (line.indexOf(o + d) === 0 && line.indexOf(o + d + d) !== 0) {
|
|
23414
|
-
closing = matches[index + 2];
|
|
23415
|
-
if (!(closing == d + c || closing == "-" + d + c || closing == "_" + d + c)) {
|
|
23416
|
-
throw new Error('Could not find matching close tag for "' + line + '".');
|
|
23417
|
-
}
|
|
23418
|
-
}
|
|
23419
|
-
self.scanLine(line);
|
|
23420
|
-
});
|
|
23421
|
-
}
|
|
23422
|
-
},
|
|
23423
|
-
parseTemplateText: function() {
|
|
23424
|
-
var str = this.templateText;
|
|
23425
|
-
var pat = this.regex;
|
|
23426
|
-
var result = pat.exec(str);
|
|
23427
|
-
var arr = [];
|
|
23428
|
-
var firstPos;
|
|
23429
|
-
while (result) {
|
|
23430
|
-
firstPos = result.index;
|
|
23431
|
-
if (firstPos !== 0) {
|
|
23432
|
-
arr.push(str.substring(0, firstPos));
|
|
23433
|
-
str = str.slice(firstPos);
|
|
23434
|
-
}
|
|
23435
|
-
arr.push(result[0]);
|
|
23436
|
-
str = str.slice(result[0].length);
|
|
23437
|
-
result = pat.exec(str);
|
|
23438
|
-
}
|
|
23439
|
-
if (str) {
|
|
23440
|
-
arr.push(str);
|
|
23441
|
-
}
|
|
23442
|
-
return arr;
|
|
23443
|
-
},
|
|
23444
|
-
_addOutput: function(line) {
|
|
23445
|
-
if (this.truncate) {
|
|
23446
|
-
line = line.replace(/^(?:\r\n|\r|\n)/, "");
|
|
23447
|
-
this.truncate = false;
|
|
23448
|
-
}
|
|
23449
|
-
if (!line) {
|
|
23450
|
-
return line;
|
|
23451
|
-
}
|
|
23452
|
-
line = line.replace(/\\/g, "\\\\");
|
|
23453
|
-
line = line.replace(/\n/g, "\\n");
|
|
23454
|
-
line = line.replace(/\r/g, "\\r");
|
|
23455
|
-
line = line.replace(/"/g, '\\"');
|
|
23456
|
-
this.source += ' ; __append("' + line + '")\n';
|
|
23457
|
-
},
|
|
23458
|
-
scanLine: function(line) {
|
|
23459
|
-
var self = this;
|
|
23460
|
-
var d = this.opts.delimiter;
|
|
23461
|
-
var o = this.opts.openDelimiter;
|
|
23462
|
-
var c = this.opts.closeDelimiter;
|
|
23463
|
-
var newLineCount = 0;
|
|
23464
|
-
newLineCount = line.split("\n").length - 1;
|
|
23465
|
-
switch (line) {
|
|
23466
|
-
case o + d:
|
|
23467
|
-
case o + d + "_":
|
|
23468
|
-
this.mode = Template.modes.EVAL;
|
|
23469
|
-
break;
|
|
23470
|
-
case o + d + "=":
|
|
23471
|
-
this.mode = Template.modes.ESCAPED;
|
|
23472
|
-
break;
|
|
23473
|
-
case o + d + "-":
|
|
23474
|
-
this.mode = Template.modes.RAW;
|
|
23475
|
-
break;
|
|
23476
|
-
case o + d + "#":
|
|
23477
|
-
this.mode = Template.modes.COMMENT;
|
|
23478
|
-
break;
|
|
23479
|
-
case o + d + d:
|
|
23480
|
-
this.mode = Template.modes.LITERAL;
|
|
23481
|
-
this.source += ' ; __append("' + line.replace(o + d + d, o + d) + '")\n';
|
|
23482
|
-
break;
|
|
23483
|
-
case d + d + c:
|
|
23484
|
-
this.mode = Template.modes.LITERAL;
|
|
23485
|
-
this.source += ' ; __append("' + line.replace(d + d + c, d + c) + '")\n';
|
|
23486
|
-
break;
|
|
23487
|
-
case d + c:
|
|
23488
|
-
case "-" + d + c:
|
|
23489
|
-
case "_" + d + c:
|
|
23490
|
-
if (this.mode == Template.modes.LITERAL) {
|
|
23491
|
-
this._addOutput(line);
|
|
23492
|
-
}
|
|
23493
|
-
this.mode = null;
|
|
23494
|
-
this.truncate = line.indexOf("-") === 0 || line.indexOf("_") === 0;
|
|
23495
|
-
break;
|
|
23496
|
-
default:
|
|
23497
|
-
if (this.mode) {
|
|
23498
|
-
switch (this.mode) {
|
|
23499
|
-
case Template.modes.EVAL:
|
|
23500
|
-
case Template.modes.ESCAPED:
|
|
23501
|
-
case Template.modes.RAW:
|
|
23502
|
-
if (line.lastIndexOf("//") > line.lastIndexOf("\n")) {
|
|
23503
|
-
line += "\n";
|
|
23504
|
-
}
|
|
23505
|
-
}
|
|
23506
|
-
switch (this.mode) {
|
|
23507
|
-
// Just executing code
|
|
23508
|
-
case Template.modes.EVAL:
|
|
23509
|
-
this.source += " ; " + line + "\n";
|
|
23510
|
-
break;
|
|
23511
|
-
// Exec, esc, and output
|
|
23512
|
-
case Template.modes.ESCAPED:
|
|
23513
|
-
this.source += " ; __append(escapeFn(" + stripSemi(line) + "))\n";
|
|
23514
|
-
break;
|
|
23515
|
-
// Exec and output
|
|
23516
|
-
case Template.modes.RAW:
|
|
23517
|
-
this.source += " ; __append(" + stripSemi(line) + ")\n";
|
|
23518
|
-
break;
|
|
23519
|
-
case Template.modes.COMMENT:
|
|
23520
|
-
break;
|
|
23521
|
-
// Literal <%% mode, append as raw output
|
|
23522
|
-
case Template.modes.LITERAL:
|
|
23523
|
-
this._addOutput(line);
|
|
23524
|
-
break;
|
|
23525
|
-
}
|
|
23526
|
-
} else {
|
|
23527
|
-
this._addOutput(line);
|
|
23528
|
-
}
|
|
23529
|
-
}
|
|
23530
|
-
if (self.opts.compileDebug && newLineCount) {
|
|
23531
|
-
this.currentLine += newLineCount;
|
|
23532
|
-
this.source += " ; __line = " + this.currentLine + "\n";
|
|
23533
|
-
}
|
|
23534
|
-
}
|
|
23535
|
-
};
|
|
23536
|
-
exports.escapeXML = utils.escapeXML;
|
|
23537
|
-
exports.__express = exports.renderFile;
|
|
23538
|
-
exports.VERSION = _VERSION_STRING;
|
|
23539
|
-
exports.name = _NAME;
|
|
23540
|
-
if (typeof window != "undefined") {
|
|
23541
|
-
window.ejs = exports;
|
|
23542
|
-
}
|
|
23543
|
-
} (ejs$1));
|
|
23544
|
-
return ejs$1;
|
|
22917
|
+
class InvalidPathParameterError extends Error {
|
|
22918
|
+
constructor(operationId, path, issue, file) {
|
|
22919
|
+
super(
|
|
22920
|
+
`Invalid path parameters in operation '${operationId}' at \`${file}\`
|
|
22921
|
+
Path: ${path}
|
|
22922
|
+
Issue: ${issue}`
|
|
22923
|
+
);
|
|
22924
|
+
this.operationId = operationId;
|
|
22925
|
+
this.path = path;
|
|
22926
|
+
this.issue = issue;
|
|
22927
|
+
this.file = file;
|
|
22928
|
+
}
|
|
23545
22929
|
}
|
|
23546
22930
|
|
|
23547
|
-
|
|
23548
|
-
|
|
22931
|
+
class InvalidSchemaError extends Error {
|
|
22932
|
+
constructor(schemaType, definitionName, context, file) {
|
|
22933
|
+
const schemaRequirement = schemaType === "body" ? "Must be a Zod schema" : "Must be a Zod object schema";
|
|
22934
|
+
super(
|
|
22935
|
+
`Invalid ${schemaType} schema in ${context} '${definitionName}' at \`${file}\`. ${schemaRequirement}.`
|
|
22936
|
+
);
|
|
22937
|
+
this.schemaType = schemaType;
|
|
22938
|
+
this.definitionName = definitionName;
|
|
22939
|
+
this.context = context;
|
|
22940
|
+
this.file = file;
|
|
22941
|
+
}
|
|
22942
|
+
}
|
|
23549
22943
|
|
|
23550
|
-
class
|
|
23551
|
-
constructor(
|
|
23552
|
-
|
|
22944
|
+
class InvalidSchemaShapeError extends Error {
|
|
22945
|
+
constructor(schemaType, definitionName, context, propertyName, invalidType, file) {
|
|
22946
|
+
const allowedTypes = schemaType === "param" ? "string-based types (ZodString, ZodLiteral<string>, ZodEnum) or ZodOptional of these types" : "string-based types (ZodString, ZodLiteral<string>, ZodEnum), ZodOptional, or ZodArray of these types";
|
|
22947
|
+
super(
|
|
22948
|
+
`Invalid ${schemaType} schema shape in ${context} '${definitionName}' at \`${file}\`
|
|
22949
|
+
Property '${propertyName}' has invalid type: ${invalidType}
|
|
22950
|
+
Allowed types: ${allowedTypes}`
|
|
22951
|
+
);
|
|
22952
|
+
this.schemaType = schemaType;
|
|
22953
|
+
this.definitionName = definitionName;
|
|
22954
|
+
this.context = context;
|
|
22955
|
+
this.propertyName = propertyName;
|
|
22956
|
+
this.invalidType = invalidType;
|
|
22957
|
+
this.file = file;
|
|
23553
22958
|
}
|
|
23554
|
-
|
|
23555
|
-
|
|
23556
|
-
|
|
23557
|
-
|
|
23558
|
-
|
|
23559
|
-
|
|
23560
|
-
|
|
23561
|
-
|
|
23562
|
-
|
|
23563
|
-
|
|
23564
|
-
|
|
22959
|
+
}
|
|
22960
|
+
|
|
22961
|
+
class InvalidStatusCodeError extends Error {
|
|
22962
|
+
constructor(statusCode, responseName, file) {
|
|
22963
|
+
const validStatusCodes = Object.values(HttpStatusCode).join(", ");
|
|
22964
|
+
super(
|
|
22965
|
+
`Invalid status code '${statusCode}' in response '${responseName}' at \`${file}\`
|
|
22966
|
+
Valid status codes: ${validStatusCodes}`
|
|
22967
|
+
);
|
|
22968
|
+
this.statusCode = statusCode;
|
|
22969
|
+
this.responseName = responseName;
|
|
22970
|
+
this.file = file;
|
|
23565
22971
|
}
|
|
23566
22972
|
}
|
|
23567
22973
|
|
|
23568
|
-
class
|
|
23569
|
-
constructor(
|
|
23570
|
-
super(
|
|
23571
|
-
|
|
23572
|
-
|
|
23573
|
-
|
|
22974
|
+
class MissingRequiredFieldError extends Error {
|
|
22975
|
+
constructor(entityType, entityName, missingField, file) {
|
|
22976
|
+
super(
|
|
22977
|
+
`Missing required field '${missingField}' in ${entityType} '${entityName}' at \`${file}\``
|
|
22978
|
+
);
|
|
22979
|
+
this.entityType = entityType;
|
|
22980
|
+
this.entityName = entityName;
|
|
22981
|
+
this.missingField = missingField;
|
|
22982
|
+
this.file = file;
|
|
23574
22983
|
}
|
|
23575
22984
|
}
|
|
23576
22985
|
|
|
23577
|
-
class
|
|
23578
|
-
|
|
23579
|
-
|
|
23580
|
-
this.
|
|
23581
|
-
|
|
22986
|
+
class DefinitionValidator {
|
|
22987
|
+
registry;
|
|
22988
|
+
constructor(registry) {
|
|
22989
|
+
this.registry = registry ?? new DefinitionRegistry();
|
|
22990
|
+
}
|
|
22991
|
+
validateOperation(operation, sourceFile) {
|
|
22992
|
+
this.registry.registerOperation(operation, sourceFile);
|
|
22993
|
+
this.validateOperationRequiredFields(operation, sourceFile);
|
|
22994
|
+
this.validateHttpMethod(operation, sourceFile);
|
|
22995
|
+
if (operation.request) {
|
|
22996
|
+
this.validateRequestSchemas(operation, sourceFile);
|
|
22997
|
+
}
|
|
22998
|
+
this.validateOperationResponses(operation, sourceFile);
|
|
22999
|
+
this.validatePathParameters(operation, sourceFile);
|
|
23000
|
+
}
|
|
23001
|
+
validateResponse(response, sourceFile) {
|
|
23002
|
+
this.registry.registerResponse(response, sourceFile);
|
|
23003
|
+
this.validateResponseRequiredFields(response, sourceFile);
|
|
23004
|
+
this.validateStatusCode(response, sourceFile);
|
|
23005
|
+
this.validateResponseSchemas(response, sourceFile);
|
|
23006
|
+
}
|
|
23007
|
+
validateOperationRequiredFields(operation, sourceFile) {
|
|
23008
|
+
if (!operation.operationId) {
|
|
23009
|
+
throw new MissingRequiredFieldError(
|
|
23010
|
+
"operation",
|
|
23011
|
+
"unknown",
|
|
23012
|
+
"operationId",
|
|
23013
|
+
sourceFile
|
|
23014
|
+
);
|
|
23015
|
+
}
|
|
23016
|
+
if (!operation.path) {
|
|
23017
|
+
throw new MissingRequiredFieldError(
|
|
23018
|
+
"operation",
|
|
23019
|
+
operation.operationId,
|
|
23020
|
+
"path",
|
|
23021
|
+
sourceFile
|
|
23022
|
+
);
|
|
23023
|
+
}
|
|
23024
|
+
if (!operation.method) {
|
|
23025
|
+
throw new MissingRequiredFieldError(
|
|
23026
|
+
"operation",
|
|
23027
|
+
operation.operationId,
|
|
23028
|
+
"method",
|
|
23029
|
+
sourceFile
|
|
23030
|
+
);
|
|
23031
|
+
}
|
|
23032
|
+
if (!operation.summary) {
|
|
23033
|
+
throw new MissingRequiredFieldError(
|
|
23034
|
+
"operation",
|
|
23035
|
+
operation.operationId,
|
|
23036
|
+
"summary",
|
|
23037
|
+
sourceFile
|
|
23038
|
+
);
|
|
23039
|
+
}
|
|
23582
23040
|
}
|
|
23583
|
-
|
|
23584
|
-
|
|
23585
|
-
|
|
23586
|
-
|
|
23587
|
-
|
|
23588
|
-
|
|
23041
|
+
validateHttpMethod(operation, sourceFile) {
|
|
23042
|
+
const validMethods = Object.values(HttpMethod);
|
|
23043
|
+
if (!validMethods.includes(operation.method)) {
|
|
23044
|
+
throw new InvalidHttpMethodError(
|
|
23045
|
+
operation.operationId,
|
|
23046
|
+
operation.method,
|
|
23047
|
+
sourceFile
|
|
23048
|
+
);
|
|
23589
23049
|
}
|
|
23590
|
-
|
|
23591
|
-
|
|
23050
|
+
}
|
|
23051
|
+
validateRequestSchemas(operation, sourceFile) {
|
|
23052
|
+
const request = operation.request;
|
|
23053
|
+
if (request.header) {
|
|
23054
|
+
this.validateSchema(
|
|
23055
|
+
request.header,
|
|
23056
|
+
"header",
|
|
23057
|
+
operation.operationId,
|
|
23058
|
+
"request",
|
|
23059
|
+
sourceFile
|
|
23060
|
+
);
|
|
23592
23061
|
}
|
|
23593
|
-
|
|
23594
|
-
|
|
23595
|
-
|
|
23596
|
-
|
|
23597
|
-
|
|
23598
|
-
|
|
23599
|
-
|
|
23062
|
+
if (request.query) {
|
|
23063
|
+
this.validateSchema(
|
|
23064
|
+
request.query,
|
|
23065
|
+
"query",
|
|
23066
|
+
operation.operationId,
|
|
23067
|
+
"request",
|
|
23068
|
+
sourceFile
|
|
23069
|
+
);
|
|
23070
|
+
}
|
|
23071
|
+
if (request.body) {
|
|
23072
|
+
this.validateSchema(
|
|
23073
|
+
request.body,
|
|
23074
|
+
"body",
|
|
23075
|
+
operation.operationId,
|
|
23076
|
+
"request",
|
|
23077
|
+
sourceFile
|
|
23078
|
+
);
|
|
23079
|
+
}
|
|
23080
|
+
if (request.param) {
|
|
23081
|
+
this.validateSchema(
|
|
23082
|
+
request.param,
|
|
23083
|
+
"param",
|
|
23084
|
+
operation.operationId,
|
|
23085
|
+
"request",
|
|
23086
|
+
sourceFile
|
|
23087
|
+
);
|
|
23088
|
+
}
|
|
23089
|
+
}
|
|
23090
|
+
validateOperationResponses(operation, sourceFile) {
|
|
23091
|
+
if (!operation.responses || operation.responses.length === 0) {
|
|
23092
|
+
throw new EmptyResponseArrayError(operation.operationId, sourceFile);
|
|
23093
|
+
}
|
|
23094
|
+
for (const response of operation.responses) {
|
|
23095
|
+
if (response instanceof HttpResponseDefinition) {
|
|
23096
|
+
continue;
|
|
23600
23097
|
}
|
|
23601
|
-
|
|
23602
|
-
|
|
23603
|
-
|
|
23604
|
-
|
|
23098
|
+
this.validateResponse(response, sourceFile);
|
|
23099
|
+
}
|
|
23100
|
+
}
|
|
23101
|
+
validateResponseRequiredFields(response, sourceFile) {
|
|
23102
|
+
if (!response.name) {
|
|
23103
|
+
throw new MissingRequiredFieldError(
|
|
23104
|
+
"response",
|
|
23105
|
+
"unknown",
|
|
23106
|
+
"name",
|
|
23107
|
+
sourceFile
|
|
23108
|
+
);
|
|
23109
|
+
}
|
|
23110
|
+
if (response.statusCode === void 0 || response.statusCode === null) {
|
|
23111
|
+
throw new MissingRequiredFieldError(
|
|
23112
|
+
"response",
|
|
23113
|
+
response.name,
|
|
23114
|
+
"statusCode",
|
|
23115
|
+
sourceFile
|
|
23116
|
+
);
|
|
23117
|
+
}
|
|
23118
|
+
if (!response.description) {
|
|
23119
|
+
throw new MissingRequiredFieldError(
|
|
23120
|
+
"response",
|
|
23121
|
+
response.name,
|
|
23122
|
+
"description",
|
|
23123
|
+
sourceFile
|
|
23124
|
+
);
|
|
23125
|
+
}
|
|
23126
|
+
}
|
|
23127
|
+
validateStatusCode(response, sourceFile) {
|
|
23128
|
+
const validStatusCodes = Object.values(HttpStatusCode);
|
|
23129
|
+
if (!validStatusCodes.includes(response.statusCode)) {
|
|
23130
|
+
throw new InvalidStatusCodeError(
|
|
23131
|
+
response.statusCode,
|
|
23132
|
+
response.name,
|
|
23133
|
+
sourceFile
|
|
23134
|
+
);
|
|
23135
|
+
}
|
|
23136
|
+
}
|
|
23137
|
+
validateResponseSchemas(response, sourceFile) {
|
|
23138
|
+
if (response.header) {
|
|
23139
|
+
this.validateSchema(
|
|
23140
|
+
response.header,
|
|
23141
|
+
"header",
|
|
23142
|
+
response.name,
|
|
23143
|
+
"response",
|
|
23144
|
+
sourceFile
|
|
23145
|
+
);
|
|
23146
|
+
}
|
|
23147
|
+
if (response.body) {
|
|
23148
|
+
this.validateSchema(
|
|
23149
|
+
response.body,
|
|
23150
|
+
"body",
|
|
23151
|
+
response.name,
|
|
23152
|
+
"response",
|
|
23153
|
+
sourceFile
|
|
23154
|
+
);
|
|
23155
|
+
}
|
|
23156
|
+
}
|
|
23157
|
+
validateSchema(schema, schemaType, definitionName, context, sourceFile) {
|
|
23158
|
+
if (schemaType === "body") {
|
|
23159
|
+
if (!(schema instanceof z.ZodType)) {
|
|
23160
|
+
throw new InvalidSchemaError(
|
|
23161
|
+
schemaType,
|
|
23162
|
+
definitionName,
|
|
23163
|
+
context,
|
|
23164
|
+
sourceFile
|
|
23605
23165
|
);
|
|
23606
23166
|
}
|
|
23607
|
-
|
|
23608
|
-
|
|
23167
|
+
return;
|
|
23168
|
+
}
|
|
23169
|
+
if (!(schema instanceof z.ZodObject)) {
|
|
23170
|
+
throw new InvalidSchemaError(
|
|
23171
|
+
schemaType,
|
|
23172
|
+
definitionName,
|
|
23173
|
+
context,
|
|
23174
|
+
sourceFile
|
|
23175
|
+
);
|
|
23176
|
+
}
|
|
23177
|
+
if (schemaType === "param") {
|
|
23178
|
+
this.validateParamShape(schema, definitionName, sourceFile);
|
|
23179
|
+
} else {
|
|
23180
|
+
this.validateHeaderOrQueryShape(
|
|
23181
|
+
schema,
|
|
23182
|
+
schemaType,
|
|
23183
|
+
definitionName,
|
|
23184
|
+
context,
|
|
23185
|
+
sourceFile
|
|
23186
|
+
);
|
|
23609
23187
|
}
|
|
23610
|
-
this.reportSuccessfulLoads(successful);
|
|
23611
23188
|
}
|
|
23612
|
-
|
|
23613
|
-
|
|
23614
|
-
|
|
23615
|
-
|
|
23616
|
-
|
|
23617
|
-
|
|
23618
|
-
|
|
23619
|
-
|
|
23620
|
-
|
|
23621
|
-
|
|
23622
|
-
|
|
23623
|
-
|
|
23624
|
-
|
|
23625
|
-
|
|
23626
|
-
|
|
23627
|
-
|
|
23628
|
-
|
|
23189
|
+
validatePathParameters(operation, sourceFile) {
|
|
23190
|
+
const pathParamMatches = operation.path.matchAll(/:([a-zA-Z0-9_]+)/g);
|
|
23191
|
+
const pathParamsSet = /* @__PURE__ */ new Set();
|
|
23192
|
+
for (const match of pathParamMatches) {
|
|
23193
|
+
const paramName = match[1];
|
|
23194
|
+
if (pathParamsSet.has(paramName)) {
|
|
23195
|
+
throw new InvalidPathParameterError(
|
|
23196
|
+
operation.operationId,
|
|
23197
|
+
operation.path,
|
|
23198
|
+
`Duplicate parameter name '${paramName}' in path`,
|
|
23199
|
+
sourceFile
|
|
23200
|
+
);
|
|
23201
|
+
}
|
|
23202
|
+
pathParamsSet.add(paramName);
|
|
23203
|
+
}
|
|
23204
|
+
const paramSchema = operation.request?.param;
|
|
23205
|
+
if (pathParamsSet.size > 0 && !paramSchema) {
|
|
23206
|
+
throw new InvalidPathParameterError(
|
|
23207
|
+
operation.operationId,
|
|
23208
|
+
operation.path,
|
|
23209
|
+
`Path contains parameters [${Array.from(pathParamsSet).join(", ")}] but request.param is not defined`,
|
|
23210
|
+
sourceFile
|
|
23211
|
+
);
|
|
23212
|
+
}
|
|
23213
|
+
if (paramSchema && paramSchema instanceof z.ZodObject) {
|
|
23214
|
+
const paramShape = paramSchema.shape;
|
|
23215
|
+
const paramKeys = new Set(Object.keys(paramShape));
|
|
23216
|
+
for (const pathParam of pathParamsSet) {
|
|
23217
|
+
if (!paramKeys.has(pathParam)) {
|
|
23218
|
+
throw new InvalidPathParameterError(
|
|
23219
|
+
operation.operationId,
|
|
23220
|
+
operation.path,
|
|
23221
|
+
`Path parameter ':${pathParam}' is not defined in request.param`,
|
|
23222
|
+
sourceFile
|
|
23223
|
+
);
|
|
23224
|
+
}
|
|
23225
|
+
}
|
|
23226
|
+
for (const paramKey of paramKeys) {
|
|
23227
|
+
if (!pathParamsSet.has(paramKey)) {
|
|
23228
|
+
throw new InvalidPathParameterError(
|
|
23229
|
+
operation.operationId,
|
|
23230
|
+
operation.path,
|
|
23231
|
+
`Parameter '${paramKey}' is defined in request.param but not used in the path`,
|
|
23232
|
+
sourceFile
|
|
23233
|
+
);
|
|
23629
23234
|
}
|
|
23630
|
-
attempts.push({
|
|
23631
|
-
path: possiblePath,
|
|
23632
|
-
error: "No default export found"
|
|
23633
|
-
});
|
|
23634
|
-
} catch (error) {
|
|
23635
|
-
attempts.push({
|
|
23636
|
-
path: possiblePath,
|
|
23637
|
-
error: error instanceof Error ? error.message : String(error)
|
|
23638
|
-
});
|
|
23639
23235
|
}
|
|
23640
23236
|
}
|
|
23641
|
-
|
|
23642
|
-
|
|
23643
|
-
|
|
23644
|
-
|
|
23645
|
-
|
|
23237
|
+
}
|
|
23238
|
+
validateHeaderOrQueryShape(schema, schemaType, definitionName, context, sourceFile) {
|
|
23239
|
+
const shape = schema.shape;
|
|
23240
|
+
for (const [propName, propSchema] of Object.entries(shape)) {
|
|
23241
|
+
if (!this.isValidHeaderOrQueryValue(propSchema)) {
|
|
23242
|
+
const typeName = this.getZodTypeName(propSchema);
|
|
23243
|
+
throw new InvalidSchemaShapeError(
|
|
23244
|
+
schemaType,
|
|
23245
|
+
definitionName,
|
|
23246
|
+
context,
|
|
23247
|
+
propName,
|
|
23248
|
+
typeName,
|
|
23249
|
+
sourceFile
|
|
23250
|
+
);
|
|
23251
|
+
}
|
|
23252
|
+
}
|
|
23253
|
+
}
|
|
23254
|
+
validateParamShape(schema, operationId, sourceFile) {
|
|
23255
|
+
const shape = schema.shape;
|
|
23256
|
+
for (const [propName, propSchema] of Object.entries(shape)) {
|
|
23257
|
+
if (!this.isValidParamValue(propSchema)) {
|
|
23258
|
+
const typeName = this.getZodTypeName(propSchema);
|
|
23259
|
+
throw new InvalidSchemaShapeError(
|
|
23260
|
+
"param",
|
|
23261
|
+
operationId,
|
|
23262
|
+
"request",
|
|
23263
|
+
propName,
|
|
23264
|
+
typeName,
|
|
23265
|
+
sourceFile
|
|
23266
|
+
);
|
|
23646
23267
|
}
|
|
23268
|
+
}
|
|
23269
|
+
}
|
|
23270
|
+
isValidHeaderOrQueryValue(schema) {
|
|
23271
|
+
if (this.isStringBasedType(schema)) {
|
|
23272
|
+
return true;
|
|
23273
|
+
}
|
|
23274
|
+
if (schema instanceof z.ZodOptional) {
|
|
23275
|
+
return this.isValidHeaderOrQueryValue(schema.unwrap());
|
|
23276
|
+
}
|
|
23277
|
+
if (schema instanceof z.ZodArray) {
|
|
23278
|
+
return this.isStringBasedType(schema.element);
|
|
23279
|
+
}
|
|
23280
|
+
return false;
|
|
23281
|
+
}
|
|
23282
|
+
isValidParamValue(schema) {
|
|
23283
|
+
if (this.isStringBasedType(schema)) {
|
|
23284
|
+
return true;
|
|
23285
|
+
}
|
|
23286
|
+
if (schema instanceof z.ZodOptional) {
|
|
23287
|
+
return this.isStringBasedType(schema.unwrap());
|
|
23288
|
+
}
|
|
23289
|
+
return false;
|
|
23290
|
+
}
|
|
23291
|
+
isStringBasedType(schema) {
|
|
23292
|
+
if (schema instanceof z.ZodString) {
|
|
23293
|
+
return true;
|
|
23294
|
+
}
|
|
23295
|
+
if (schema instanceof z.ZodLiteral && typeof schema.value === "string") {
|
|
23296
|
+
return true;
|
|
23297
|
+
}
|
|
23298
|
+
if (schema instanceof z.ZodEnum) {
|
|
23299
|
+
return true;
|
|
23300
|
+
}
|
|
23301
|
+
const typeName = schema.constructor.name;
|
|
23302
|
+
const stringFormatTypes = [
|
|
23303
|
+
"ZodULID",
|
|
23304
|
+
"ZodUUID",
|
|
23305
|
+
"ZodUUIDv4",
|
|
23306
|
+
"ZodUUIDv7",
|
|
23307
|
+
"ZodUUIDv8",
|
|
23308
|
+
"ZodEmail",
|
|
23309
|
+
"ZodURL",
|
|
23310
|
+
"ZodCUID",
|
|
23311
|
+
"ZodCUID2",
|
|
23312
|
+
"ZodNanoID",
|
|
23313
|
+
"ZodBase64",
|
|
23314
|
+
"ZodBase64URL",
|
|
23315
|
+
"ZodEmoji",
|
|
23316
|
+
"ZodIPv4",
|
|
23317
|
+
"ZodIPv6",
|
|
23318
|
+
"ZodCIDRv4",
|
|
23319
|
+
"ZodCIDRv6",
|
|
23320
|
+
"ZodE164",
|
|
23321
|
+
"ZodJWT",
|
|
23322
|
+
"ZodASCII",
|
|
23323
|
+
"ZodUTF8",
|
|
23324
|
+
"ZodLowercase",
|
|
23325
|
+
"ZodGUID",
|
|
23326
|
+
"ZodISODate",
|
|
23327
|
+
"ZodISOTime",
|
|
23328
|
+
"ZodISODateTime",
|
|
23329
|
+
"ZodISODuration"
|
|
23330
|
+
];
|
|
23331
|
+
if (stringFormatTypes.includes(typeName)) {
|
|
23332
|
+
return true;
|
|
23333
|
+
}
|
|
23334
|
+
if (schema._def && schema._def.typeName && schema._def.typeName.includes("String")) {
|
|
23335
|
+
return true;
|
|
23336
|
+
}
|
|
23337
|
+
return false;
|
|
23338
|
+
}
|
|
23339
|
+
getZodTypeName(schema) {
|
|
23340
|
+
const typeName = schema.constructor.name;
|
|
23341
|
+
if (schema instanceof z.ZodOptional) {
|
|
23342
|
+
return `ZodOptional<${this.getZodTypeName(schema.unwrap())}>`;
|
|
23343
|
+
}
|
|
23344
|
+
if (schema instanceof z.ZodArray) {
|
|
23345
|
+
return `ZodArray<${this.getZodTypeName(schema.element)}>`;
|
|
23346
|
+
}
|
|
23347
|
+
if (schema instanceof z.ZodLiteral) {
|
|
23348
|
+
return `ZodLiteral<${typeof schema.value}>`;
|
|
23349
|
+
}
|
|
23350
|
+
return typeName;
|
|
23351
|
+
}
|
|
23352
|
+
getRegistry() {
|
|
23353
|
+
return this.registry;
|
|
23354
|
+
}
|
|
23355
|
+
}
|
|
23356
|
+
|
|
23357
|
+
class InvalidSharedDirError extends Error {
|
|
23358
|
+
constructor(explanation) {
|
|
23359
|
+
super("Invalid shared dir");
|
|
23360
|
+
this.explanation = explanation;
|
|
23361
|
+
}
|
|
23362
|
+
}
|
|
23363
|
+
|
|
23364
|
+
class ResourceReader {
|
|
23365
|
+
constructor(config) {
|
|
23366
|
+
this.config = config;
|
|
23367
|
+
}
|
|
23368
|
+
async getResources() {
|
|
23369
|
+
const contents = fs.readdirSync(this.config.sourceDir, {
|
|
23370
|
+
withFileTypes: true
|
|
23371
|
+
});
|
|
23372
|
+
const result = {
|
|
23373
|
+
entityResources: {},
|
|
23374
|
+
sharedResponseResources: []
|
|
23647
23375
|
};
|
|
23376
|
+
const validator = new DefinitionValidator();
|
|
23377
|
+
if (fs.existsSync(this.config.sharedSourceDir)) {
|
|
23378
|
+
const sharedStats = fs.statSync(this.config.sharedSourceDir);
|
|
23379
|
+
if (!sharedStats.isDirectory()) {
|
|
23380
|
+
throw new InvalidSharedDirError(
|
|
23381
|
+
`'${this.config.sharedSourceDir}' is a file, not a directory`
|
|
23382
|
+
);
|
|
23383
|
+
}
|
|
23384
|
+
result.sharedResponseResources = await this.getSharedResponseResources(validator);
|
|
23385
|
+
console.info(
|
|
23386
|
+
`Found '${result.sharedResponseResources.length}' shared responses in '${this.config.sharedSourceDir}'`
|
|
23387
|
+
);
|
|
23388
|
+
} else {
|
|
23389
|
+
console.info(
|
|
23390
|
+
`No shared directory found at '${this.config.sharedSourceDir}'`
|
|
23391
|
+
);
|
|
23392
|
+
}
|
|
23393
|
+
const normalizedSharedPath = path__default.resolve(this.config.sharedSourceDir);
|
|
23394
|
+
for (const content of contents) {
|
|
23395
|
+
if (!content.isDirectory()) {
|
|
23396
|
+
console.info(`Skipping '${content.name}' as it is not a directory`);
|
|
23397
|
+
continue;
|
|
23398
|
+
}
|
|
23399
|
+
const entityName = content.name;
|
|
23400
|
+
const entitySourceDir = path__default.resolve(this.config.sourceDir, entityName);
|
|
23401
|
+
if (entitySourceDir === normalizedSharedPath || normalizedSharedPath.startsWith(entitySourceDir + path__default.sep)) {
|
|
23402
|
+
console.info(
|
|
23403
|
+
`Skipping '${content.name}' as it is or contains the shared directory`
|
|
23404
|
+
);
|
|
23405
|
+
continue;
|
|
23406
|
+
}
|
|
23407
|
+
const responseResources = await this.getEntityResponseResources(
|
|
23408
|
+
entitySourceDir,
|
|
23409
|
+
entityName,
|
|
23410
|
+
validator
|
|
23411
|
+
);
|
|
23412
|
+
const operationResources = await this.getEntityOperationResources(
|
|
23413
|
+
entitySourceDir,
|
|
23414
|
+
entityName,
|
|
23415
|
+
validator,
|
|
23416
|
+
[...result.sharedResponseResources, ...responseResources]
|
|
23417
|
+
);
|
|
23418
|
+
result.entityResources[entityName] = {
|
|
23419
|
+
operations: operationResources,
|
|
23420
|
+
responses: responseResources
|
|
23421
|
+
};
|
|
23422
|
+
console.info(
|
|
23423
|
+
`Found '${operationResources.length}' operation definitions for entity '${entityName}'`
|
|
23424
|
+
);
|
|
23425
|
+
if (responseResources.length > 0) {
|
|
23426
|
+
console.info(
|
|
23427
|
+
`Found '${responseResources.length}' response definitions for entity '${entityName}'`
|
|
23428
|
+
);
|
|
23429
|
+
}
|
|
23430
|
+
}
|
|
23431
|
+
return result;
|
|
23648
23432
|
}
|
|
23649
|
-
|
|
23650
|
-
|
|
23651
|
-
|
|
23652
|
-
|
|
23653
|
-
|
|
23654
|
-
|
|
23655
|
-
|
|
23656
|
-
|
|
23657
|
-
|
|
23658
|
-
paths.push(`@rexeus/${pluginName}`);
|
|
23659
|
-
break;
|
|
23660
|
-
case "local":
|
|
23661
|
-
paths.push(pluginName);
|
|
23662
|
-
break;
|
|
23433
|
+
scanDirectoryRecursively(dir) {
|
|
23434
|
+
const files = [];
|
|
23435
|
+
const contents = fs.readdirSync(dir, { withFileTypes: true });
|
|
23436
|
+
for (const content of contents) {
|
|
23437
|
+
const fullPath = path__default.join(dir, content.name);
|
|
23438
|
+
if (content.isDirectory()) {
|
|
23439
|
+
files.push(...this.scanDirectoryRecursively(fullPath));
|
|
23440
|
+
} else if (content.isFile() && content.name.endsWith(".ts")) {
|
|
23441
|
+
files.push(fullPath);
|
|
23663
23442
|
}
|
|
23664
23443
|
}
|
|
23665
|
-
return
|
|
23444
|
+
return files;
|
|
23666
23445
|
}
|
|
23667
|
-
|
|
23668
|
-
|
|
23669
|
-
|
|
23670
|
-
|
|
23671
|
-
|
|
23672
|
-
|
|
23673
|
-
|
|
23674
|
-
console.info(
|
|
23446
|
+
async getSharedResponseResources(validator) {
|
|
23447
|
+
const files = this.scanDirectoryRecursively(this.config.sharedSourceDir);
|
|
23448
|
+
const sharedResponseResources = [];
|
|
23449
|
+
for (const sourceFile of files) {
|
|
23450
|
+
const sourceFileName = path__default.basename(sourceFile);
|
|
23451
|
+
const definition = await import(sourceFile);
|
|
23452
|
+
if (!definition.default) {
|
|
23453
|
+
console.info(
|
|
23454
|
+
`Skipping '${sourceFile}' as it does not have a default export`
|
|
23455
|
+
);
|
|
23456
|
+
continue;
|
|
23457
|
+
}
|
|
23458
|
+
if (!(definition.default instanceof HttpResponseDefinition)) {
|
|
23459
|
+
console.info(
|
|
23460
|
+
`Skipping '${sourceFile}' as it is not an instance of HttpResponseDefinition`
|
|
23461
|
+
);
|
|
23462
|
+
continue;
|
|
23463
|
+
}
|
|
23464
|
+
validator.validateResponse(definition.default, sourceFile);
|
|
23465
|
+
const outputDir = this.config.sharedOutputDir;
|
|
23466
|
+
const outputFileName = `${definition.default.name}Response.ts`;
|
|
23467
|
+
const outputFile = path__default.join(outputDir, outputFileName);
|
|
23468
|
+
sharedResponseResources.push({
|
|
23469
|
+
...definition.default,
|
|
23470
|
+
sourceDir: this.config.sharedSourceDir,
|
|
23471
|
+
sourceFile,
|
|
23472
|
+
sourceFileName,
|
|
23473
|
+
outputFile,
|
|
23474
|
+
outputFileName,
|
|
23475
|
+
outputDir
|
|
23476
|
+
});
|
|
23477
|
+
}
|
|
23478
|
+
return sharedResponseResources;
|
|
23479
|
+
}
|
|
23480
|
+
async getEntityOperationResources(sourceDir, entityName, validator, referencedResponses) {
|
|
23481
|
+
const files = this.scanDirectoryRecursively(sourceDir);
|
|
23482
|
+
const definitions = [];
|
|
23483
|
+
for (const sourceFile of files) {
|
|
23484
|
+
const sourceFileName = path__default.basename(sourceFile);
|
|
23485
|
+
const definition = await import(sourceFile);
|
|
23486
|
+
if (!definition.default) {
|
|
23487
|
+
console.info(
|
|
23488
|
+
`Skipping '${sourceFile}' as it does not have a default export`
|
|
23489
|
+
);
|
|
23490
|
+
continue;
|
|
23675
23491
|
}
|
|
23492
|
+
if (!(definition.default instanceof HttpOperationDefinition)) {
|
|
23493
|
+
console.info(
|
|
23494
|
+
`Skipping '${sourceFile}' as it is not an instance of HttpOperationDefinition`
|
|
23495
|
+
);
|
|
23496
|
+
continue;
|
|
23497
|
+
}
|
|
23498
|
+
validator.validateOperation(definition.default, sourceFile);
|
|
23499
|
+
const { operationId } = definition.default;
|
|
23500
|
+
const outputDir = path__default.join(this.config.outputDir, entityName);
|
|
23501
|
+
const outputRequestFileName = `${operationId}Request.ts`;
|
|
23502
|
+
const outputRequestFile = path__default.join(outputDir, outputRequestFileName);
|
|
23503
|
+
const outputResponseFileName = `${operationId}Response.ts`;
|
|
23504
|
+
const outputResponseFile = path__default.join(outputDir, outputResponseFileName);
|
|
23505
|
+
const outputRequestValidationFileName = `${operationId}RequestValidator.ts`;
|
|
23506
|
+
const outputRequestValidationFile = path__default.join(
|
|
23507
|
+
outputDir,
|
|
23508
|
+
outputRequestValidationFileName
|
|
23509
|
+
);
|
|
23510
|
+
const outputResponseValidationFileName = `${operationId}ResponseValidator.ts`;
|
|
23511
|
+
const outputResponseValidationFile = path__default.join(
|
|
23512
|
+
outputDir,
|
|
23513
|
+
outputResponseValidationFileName
|
|
23514
|
+
);
|
|
23515
|
+
const outputClientFileName = `${operationId}Client.ts`;
|
|
23516
|
+
const outputClientFile = path__default.join(outputDir, outputClientFileName);
|
|
23517
|
+
const operationResource = {
|
|
23518
|
+
sourceDir,
|
|
23519
|
+
sourceFile,
|
|
23520
|
+
sourceFileName,
|
|
23521
|
+
definition: {
|
|
23522
|
+
...definition.default,
|
|
23523
|
+
responses: []
|
|
23524
|
+
},
|
|
23525
|
+
outputDir,
|
|
23526
|
+
entityName,
|
|
23527
|
+
outputRequestFile,
|
|
23528
|
+
outputResponseFile,
|
|
23529
|
+
outputResponseValidationFile,
|
|
23530
|
+
outputRequestValidationFile,
|
|
23531
|
+
outputRequestFileName,
|
|
23532
|
+
outputRequestValidationFileName,
|
|
23533
|
+
outputResponseFileName,
|
|
23534
|
+
outputResponseValidationFileName,
|
|
23535
|
+
outputClientFile,
|
|
23536
|
+
outputClientFileName
|
|
23537
|
+
};
|
|
23538
|
+
if (!definition.default.responses) {
|
|
23539
|
+
throw new Error(
|
|
23540
|
+
`Operation '${operationId}' does not have any responses`
|
|
23541
|
+
);
|
|
23542
|
+
}
|
|
23543
|
+
for (const response of definition.default.responses) {
|
|
23544
|
+
if (referencedResponses.some((ref) => ref.name === response.name)) {
|
|
23545
|
+
const referencedResponse = {
|
|
23546
|
+
...response,
|
|
23547
|
+
statusCodeName: HttpStatusCodeNameMap[response.statusCode],
|
|
23548
|
+
isReference: true
|
|
23549
|
+
};
|
|
23550
|
+
operationResource.definition.responses.push(referencedResponse);
|
|
23551
|
+
} else {
|
|
23552
|
+
const extendedResponse = {
|
|
23553
|
+
...response,
|
|
23554
|
+
statusCodeName: HttpStatusCodeNameMap[response.statusCode],
|
|
23555
|
+
isReference: false
|
|
23556
|
+
};
|
|
23557
|
+
operationResource.definition.responses.push(extendedResponse);
|
|
23558
|
+
}
|
|
23559
|
+
}
|
|
23560
|
+
definitions.push(operationResource);
|
|
23676
23561
|
}
|
|
23562
|
+
return definitions;
|
|
23563
|
+
}
|
|
23564
|
+
async getEntityResponseResources(sourceDir, entityName, validator) {
|
|
23565
|
+
const files = this.scanDirectoryRecursively(sourceDir);
|
|
23566
|
+
const responseResources = [];
|
|
23567
|
+
for (const sourceFile of files) {
|
|
23568
|
+
const sourceFileName = path__default.basename(sourceFile);
|
|
23569
|
+
const definition = await import(sourceFile);
|
|
23570
|
+
if (!definition.default) {
|
|
23571
|
+
continue;
|
|
23572
|
+
}
|
|
23573
|
+
if (!(definition.default instanceof HttpResponseDefinition)) {
|
|
23574
|
+
continue;
|
|
23575
|
+
}
|
|
23576
|
+
validator.validateResponse(definition.default, sourceFile);
|
|
23577
|
+
const outputFileName = `${definition.default.name}Response.ts`;
|
|
23578
|
+
const outputFile = path__default.join(
|
|
23579
|
+
this.config.outputDir,
|
|
23580
|
+
entityName,
|
|
23581
|
+
outputFileName
|
|
23582
|
+
);
|
|
23583
|
+
responseResources.push({
|
|
23584
|
+
...definition.default,
|
|
23585
|
+
sourceDir,
|
|
23586
|
+
sourceFile,
|
|
23587
|
+
sourceFileName,
|
|
23588
|
+
outputFile,
|
|
23589
|
+
outputFileName,
|
|
23590
|
+
outputDir: path__default.join(this.config.outputDir, entityName),
|
|
23591
|
+
entityName
|
|
23592
|
+
});
|
|
23593
|
+
}
|
|
23594
|
+
return responseResources;
|
|
23677
23595
|
}
|
|
23678
23596
|
}
|
|
23679
23597
|
|
|
@@ -23687,6 +23605,8 @@ class Generator {
|
|
|
23687
23605
|
indexFileGenerator;
|
|
23688
23606
|
resourceReader = null;
|
|
23689
23607
|
prettier = null;
|
|
23608
|
+
inputDir = "";
|
|
23609
|
+
sharedInputDir = "";
|
|
23690
23610
|
outputDir = "";
|
|
23691
23611
|
sourceDir = "";
|
|
23692
23612
|
sharedSourceDir = "";
|
|
@@ -23702,13 +23622,22 @@ class Generator {
|
|
|
23702
23622
|
*/
|
|
23703
23623
|
async generate(definitionDir, outputDir, config) {
|
|
23704
23624
|
console.info("Starting generation...");
|
|
23705
|
-
this.initializeDirectories(definitionDir, outputDir);
|
|
23625
|
+
this.initializeDirectories(definitionDir, outputDir, config?.shared);
|
|
23706
23626
|
if (config?.clean ?? true) {
|
|
23707
23627
|
console.info("Cleaning output directory...");
|
|
23708
23628
|
fs.rmSync(this.outputDir, { recursive: true, force: true });
|
|
23709
23629
|
}
|
|
23710
23630
|
fs.mkdirSync(this.outputDir, { recursive: true });
|
|
23711
23631
|
fs.mkdirSync(this.sharedOutputDir, { recursive: true });
|
|
23632
|
+
console.info(
|
|
23633
|
+
`Copying definitions from '${this.inputDir}' to '${this.sourceDir}'...`
|
|
23634
|
+
);
|
|
23635
|
+
fs.cpSync(this.inputDir, this.sourceDir, {
|
|
23636
|
+
recursive: true,
|
|
23637
|
+
filter: (src) => {
|
|
23638
|
+
return src.endsWith(".ts") || src.endsWith(".js") || src.endsWith(".json") || src.endsWith(".mjs") || src.endsWith(".cjs") || fs.statSync(src).isDirectory();
|
|
23639
|
+
}
|
|
23640
|
+
});
|
|
23712
23641
|
await this.pluginLoader.loadPlugins(config);
|
|
23713
23642
|
this.resourceReader = new ResourceReader({
|
|
23714
23643
|
sourceDir: this.sourceDir,
|
|
@@ -23766,22 +23695,24 @@ class Generator {
|
|
|
23766
23695
|
`Generated files: ${this.contextBuilder.getGeneratedFiles().length}`
|
|
23767
23696
|
);
|
|
23768
23697
|
}
|
|
23769
|
-
initializeDirectories(definitionDir, outputDir) {
|
|
23770
|
-
this.
|
|
23698
|
+
initializeDirectories(definitionDir, outputDir, sharedDir) {
|
|
23699
|
+
this.inputDir = definitionDir;
|
|
23700
|
+
this.sharedInputDir = sharedDir ?? path__default.join(definitionDir, "shared");
|
|
23771
23701
|
this.outputDir = outputDir;
|
|
23772
|
-
this.sharedSourceDir = path__default.join(definitionDir, "shared");
|
|
23773
23702
|
this.sharedOutputDir = path__default.join(outputDir, "shared");
|
|
23703
|
+
this.sourceDir = path__default.join(this.outputDir, "definition");
|
|
23704
|
+
const inputToSharedDirRelative = path__default.relative(
|
|
23705
|
+
this.inputDir,
|
|
23706
|
+
this.sharedInputDir
|
|
23707
|
+
);
|
|
23708
|
+
this.sharedSourceDir = path__default.join(this.sourceDir, inputToSharedDirRelative);
|
|
23774
23709
|
}
|
|
23775
23710
|
}
|
|
23776
23711
|
|
|
23777
|
-
var version = "0.0.2";
|
|
23778
|
-
var packageJson = {
|
|
23779
|
-
version: version};
|
|
23780
|
-
|
|
23781
23712
|
const program = new Command();
|
|
23782
23713
|
const execDir = process.cwd();
|
|
23783
23714
|
program.name("@rexeus/typeweaver").description("Type-safe API framework with code generation for TypeScript").version(packageJson.version);
|
|
23784
|
-
program.command("generate").description("Generate types, validators, and clients from API definitions").option("-i, --input <inputDir>", "path to definition directory").option("-o, --output <outputDir>", "output directory for generated files").option("-c, --config <configFile>", "path to configuration file").option("-p, --plugins <plugins>", "comma-separated list of plugins to use").option("--prettier", "format generated code with Prettier (default: true)").option("--no-prettier", "disable Prettier formatting").option("--clean", "clean output directory before generation (default: true)").option("--no-clean", "disable cleaning output directory").action(async (options) => {
|
|
23715
|
+
program.command("generate").description("Generate types, validators, and clients from API definitions").option("-i, --input <inputDir>", "path to definition directory").option("-o, --output <outputDir>", "output directory for generated files").option("-s, --shared <path>", "path to shared definitions directory").option("-c, --config <configFile>", "path to configuration file").option("-p, --plugins <plugins>", "comma-separated list of plugins to use").option("--prettier", "format generated code with Prettier (default: true)").option("--no-prettier", "disable Prettier formatting").option("--clean", "clean output directory before generation (default: true)").option("--no-clean", "disable cleaning output directory").action(async (options) => {
|
|
23785
23716
|
let config = {};
|
|
23786
23717
|
if (options.config) {
|
|
23787
23718
|
const configPath = path__default.isAbsolute(options.config) ? options.config : path__default.join(execDir, options.config);
|
|
@@ -23798,6 +23729,7 @@ program.command("generate").description("Generate types, validators, and clients
|
|
|
23798
23729
|
}
|
|
23799
23730
|
const inputDir = options.input ?? config.input;
|
|
23800
23731
|
const outputDir = options.output ?? config.output;
|
|
23732
|
+
const sharedDir = options.shared ?? config.shared;
|
|
23801
23733
|
if (!inputDir) {
|
|
23802
23734
|
throw new Error(
|
|
23803
23735
|
"No input directory provided. Use --input or specify in config file."
|
|
@@ -23810,9 +23742,11 @@ program.command("generate").description("Generate types, validators, and clients
|
|
|
23810
23742
|
}
|
|
23811
23743
|
const resolvedInputDir = path__default.isAbsolute(inputDir) ? inputDir : path__default.join(execDir, inputDir);
|
|
23812
23744
|
const resolvedOutputDir = path__default.isAbsolute(outputDir) ? outputDir : path__default.join(execDir, outputDir);
|
|
23745
|
+
const resolvedSharedDir = sharedDir ? path__default.isAbsolute(sharedDir) ? sharedDir : path__default.join(execDir, sharedDir) : void 0;
|
|
23813
23746
|
const finalConfig = {
|
|
23814
23747
|
input: resolvedInputDir,
|
|
23815
23748
|
output: resolvedOutputDir,
|
|
23749
|
+
shared: resolvedSharedDir,
|
|
23816
23750
|
prettier: options.prettier ?? config.prettier ?? true,
|
|
23817
23751
|
clean: options.clean ?? config.clean ?? true
|
|
23818
23752
|
};
|
|
@@ -23824,7 +23758,7 @@ program.command("generate").description("Generate types, validators, and clients
|
|
|
23824
23758
|
const generator = new Generator();
|
|
23825
23759
|
return generator.generate(resolvedInputDir, resolvedOutputDir, finalConfig);
|
|
23826
23760
|
});
|
|
23827
|
-
program.command("init").description("Initialize a new
|
|
23761
|
+
program.command("init").description("Initialize a new typeweaver project (coming soon)").action(() => {
|
|
23828
23762
|
console.log("The init command is coming soon!");
|
|
23829
23763
|
});
|
|
23830
23764
|
program.parse(process.argv);
|