toolcraft-openapi 0.0.57 → 0.0.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/generate.d.ts +1 -0
- package/dist/bin/generate.js +80 -4
- package/package.json +5 -2
package/dist/bin/generate.d.ts
CHANGED
package/dist/bin/generate.js
CHANGED
|
@@ -19,6 +19,7 @@ const DEFAULT_OPTIONS = {
|
|
|
19
19
|
diff: false,
|
|
20
20
|
input: "openapi.json",
|
|
21
21
|
inspect: false,
|
|
22
|
+
lockPath: "openapi.lock",
|
|
22
23
|
outputFormat: "terminal",
|
|
23
24
|
outputDir: "src/generated"
|
|
24
25
|
};
|
|
@@ -27,6 +28,7 @@ const HELP_TEXT = `Usage: toolcraft-openapi-generate [options]
|
|
|
27
28
|
Options:
|
|
28
29
|
--input <path-or-url> OpenAPI document to read (default: openapi.json)
|
|
29
30
|
--output <dir> Directory for generated command files (default: src/generated)
|
|
31
|
+
--lock <path> Lock file path (default: openapi.lock)
|
|
30
32
|
--check Exit non-zero if generated output would change
|
|
31
33
|
--diff Print a diff of generated changes without writing files
|
|
32
34
|
--inspect Inspect route compatibility without writing files
|
|
@@ -113,18 +115,27 @@ export async function syncGeneratedClient(options, services) {
|
|
|
113
115
|
const effectiveConfig = hasErrorDiagnostics(diagnostics) ? undefined : configResult.config;
|
|
114
116
|
const generatedFiles = generate(document, { specSha, config: effectiveConfig });
|
|
115
117
|
const outputDir = path.resolve(services.cwd, options.outputDir);
|
|
118
|
+
const lockPath = path.resolve(services.cwd, options.lockPath);
|
|
119
|
+
const currentLockContents = await readOpenApiLockText(services.fs, lockPath);
|
|
120
|
+
const desiredLockContents = stringifyOpenApiLock({ specSha });
|
|
116
121
|
const currentFiles = await readGeneratedFiles(services.fs, outputDir);
|
|
117
122
|
const desiredFiles = new Map([
|
|
118
123
|
...generatedFiles.map((file) => [path.resolve(outputDir, file.path), file.contents]),
|
|
119
124
|
...createDownloadedSpecFiles(options.input, sourceText).map((file) => [path.resolve(outputDir, file.path), file.contents])
|
|
120
125
|
]);
|
|
121
126
|
const updatedFiles = collectUpdatedFiles(currentFiles, desiredFiles);
|
|
127
|
+
const updatedLockFile = currentLockContents === desiredLockContents
|
|
128
|
+
? undefined
|
|
129
|
+
: { path: lockPath, contents: desiredLockContents, previousContents: currentLockContents };
|
|
122
130
|
const deletedFiles = collectDeletedFiles(currentFiles, desiredFiles);
|
|
123
|
-
const drifted = updatedFiles.length > 0 || deletedFiles.length > 0;
|
|
131
|
+
const drifted = updatedFiles.length > 0 || updatedLockFile !== undefined || deletedFiles.length > 0;
|
|
124
132
|
if (!options.check && !options.diff && drifted && !hasErrorDiagnostics(diagnostics)) {
|
|
125
133
|
try {
|
|
126
134
|
await writeGeneratedFiles(services.fs, outputDir, updatedFiles);
|
|
127
135
|
await deleteGeneratedFiles(services.fs, outputDir, deletedFiles);
|
|
136
|
+
if (updatedLockFile !== undefined) {
|
|
137
|
+
await writeOpenApiLock(services.fs, lockPath, { specSha });
|
|
138
|
+
}
|
|
128
139
|
}
|
|
129
140
|
catch (error) {
|
|
130
141
|
await restoreGeneratedFiles(services.fs, outputDir, currentFiles, updatedFiles, deletedFiles);
|
|
@@ -137,8 +148,8 @@ export async function syncGeneratedClient(options, services) {
|
|
|
137
148
|
diagnostics,
|
|
138
149
|
drifted,
|
|
139
150
|
specSha,
|
|
140
|
-
updatedFiles,
|
|
141
|
-
updatedFileCount: updatedFiles.length
|
|
151
|
+
updatedFiles: updatedLockFile === undefined ? updatedFiles : [...updatedFiles, updatedLockFile],
|
|
152
|
+
updatedFileCount: updatedFiles.length + (updatedLockFile === undefined ? 0 : 1)
|
|
142
153
|
};
|
|
143
154
|
}
|
|
144
155
|
async function readAdjacentToolcraftConfig(input, services) {
|
|
@@ -243,7 +254,10 @@ function parseGenerateCliArgs(argv) {
|
|
|
243
254
|
options.inspect = true;
|
|
244
255
|
continue;
|
|
245
256
|
}
|
|
246
|
-
if (argument === "--input" ||
|
|
257
|
+
if (argument === "--input" ||
|
|
258
|
+
argument === "--output" ||
|
|
259
|
+
argument === "--lock" ||
|
|
260
|
+
argument === "--output-format") {
|
|
247
261
|
const value = argv[index + 1];
|
|
248
262
|
if (value === undefined) {
|
|
249
263
|
throw new UserError(`Missing value for ${JSON.stringify(argument)}.`);
|
|
@@ -260,6 +274,10 @@ function parseGenerateCliArgs(argv) {
|
|
|
260
274
|
assignOptionValue(options, "--output", argument.slice("--output=".length));
|
|
261
275
|
continue;
|
|
262
276
|
}
|
|
277
|
+
if (argument.startsWith("--lock=")) {
|
|
278
|
+
assignOptionValue(options, "--lock", argument.slice("--lock=".length));
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
263
281
|
if (argument.startsWith("--output-format=")) {
|
|
264
282
|
assignOptionValue(options, "--output-format", argument.slice("--output-format=".length));
|
|
265
283
|
continue;
|
|
@@ -283,8 +301,57 @@ function assignOptionValue(options, argument, value) {
|
|
|
283
301
|
options.outputFormat = value;
|
|
284
302
|
return;
|
|
285
303
|
}
|
|
304
|
+
if (argument === "--lock") {
|
|
305
|
+
options.lockPath = value;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
286
308
|
options.outputDir = value;
|
|
287
309
|
}
|
|
310
|
+
async function readOpenApiLockText(fs, lockPath) {
|
|
311
|
+
try {
|
|
312
|
+
const contents = await fs.readFile(lockPath, "utf8");
|
|
313
|
+
parseOpenApiLock(contents, lockPath);
|
|
314
|
+
return contents;
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
if (isNotFoundError(error)) {
|
|
318
|
+
return undefined;
|
|
319
|
+
}
|
|
320
|
+
throw error;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function parseOpenApiLock(contents, lockPath) {
|
|
324
|
+
let parsed;
|
|
325
|
+
try {
|
|
326
|
+
parsed = JSON.parse(contents);
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
throw new UserError(`Lock file ${JSON.stringify(lockPath)} is not valid JSON: ${getErrorMessage(error)}.`, { cause: error });
|
|
330
|
+
}
|
|
331
|
+
if (typeof parsed !== "object" ||
|
|
332
|
+
parsed === null ||
|
|
333
|
+
!("version" in parsed) ||
|
|
334
|
+
parsed.version !== 1 ||
|
|
335
|
+
!("specSha" in parsed) ||
|
|
336
|
+
typeof parsed.specSha !== "string" ||
|
|
337
|
+
parsed.specSha.length === 0) {
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
return { specSha: parsed.specSha };
|
|
341
|
+
}
|
|
342
|
+
function stringifyOpenApiLock(lock) {
|
|
343
|
+
return `${JSON.stringify({ version: 1, specSha: lock.specSha }, null, 2)}\n`;
|
|
344
|
+
}
|
|
345
|
+
async function writeOpenApiLock(fs, lockPath, lock) {
|
|
346
|
+
await fs.mkdir(path.dirname(lockPath), { recursive: true });
|
|
347
|
+
try {
|
|
348
|
+
await fs.writeFile(lockPath, stringifyOpenApiLock(lock), "utf8");
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
const code = getErrorCode(error);
|
|
352
|
+
throw new UserError(`Failed to write lock file ${JSON.stringify(lockPath)}${code === undefined ? "" : ` (${code})`}: ${getErrorMessage(error)}`, { cause: error });
|
|
353
|
+
}
|
|
354
|
+
}
|
|
288
355
|
function createSpecSha(sourceText) {
|
|
289
356
|
return `sha256:${createHash("sha256").update(sourceText).digest("hex")}`;
|
|
290
357
|
}
|
|
@@ -446,6 +513,15 @@ function isNotFoundError(error) {
|
|
|
446
513
|
function isAlreadyExistsError(error) {
|
|
447
514
|
return hasOwnErrorCode(error, "EEXIST");
|
|
448
515
|
}
|
|
516
|
+
function getErrorCode(error) {
|
|
517
|
+
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
518
|
+
return undefined;
|
|
519
|
+
}
|
|
520
|
+
return typeof error.code === "string" ? error.code : undefined;
|
|
521
|
+
}
|
|
522
|
+
function getErrorMessage(error) {
|
|
523
|
+
return error instanceof Error ? error.message : String(error);
|
|
524
|
+
}
|
|
449
525
|
function isDirectExecution(moduleUrl, argv) {
|
|
450
526
|
const entryPoint = argv[1];
|
|
451
527
|
if (entryPoint === undefined) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft-openapi",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.59",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -28,8 +28,11 @@
|
|
|
28
28
|
"toolcraft-openapi-generate": "dist/bin/generate.js"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"toolcraft": "0.0.
|
|
31
|
+
"toolcraft": "0.0.59",
|
|
32
32
|
"auth-store": "^0.0.1",
|
|
33
|
+
"fast-string-width": "^3.0.2",
|
|
34
|
+
"fast-wrap-ansi": "^0.2.0",
|
|
35
|
+
"sisteransi": "^1.0.5",
|
|
33
36
|
"yaml": "^2.8.2"
|
|
34
37
|
},
|
|
35
38
|
"engines": {
|