@sdk-it/core 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -40
- package/dist/index.js.map +2 -2
- package/dist/lib/file-system.d.ts +30 -5
- package/dist/lib/file-system.d.ts.map +1 -1
- package/dist/lib/file-system.js +78 -23
- package/dist/lib/file-system.js.map +3 -3
- package/dist/lib/file-system.test.d.ts +2 -0
- package/dist/lib/file-system.test.d.ts.map +1 -0
- package/dist/lib/file-system.test.js +357 -0
- package/dist/lib/file-system.test.js.map +7 -0
- package/dist/lib/paths.d.ts +5 -1
- package/dist/lib/paths.d.ts.map +1 -1
- package/dist/lib/paths.js +6 -6
- package/dist/lib/paths.js.map +2 -2
- package/dist/lib/ref.d.ts +5 -2
- package/dist/lib/ref.d.ts.map +1 -1
- package/dist/lib/ref.js +72 -3
- package/dist/lib/ref.js.map +3 -3
- package/dist/lib/utils.d.ts +21 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +62 -0
- package/dist/lib/utils.js.map +7 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,6 @@
|
|
|
1
|
-
import type ts from 'typescript';
|
|
2
|
-
import type { TypeDeriver } from './lib/deriver.ts';
|
|
3
|
-
import type { ResponseItem } from './lib/paths.ts';
|
|
4
1
|
export * from './lib/deriver.js';
|
|
5
2
|
export * from './lib/paths.js';
|
|
6
3
|
export * from './lib/program.js';
|
|
7
4
|
export * from './lib/ref.js';
|
|
8
|
-
export
|
|
9
|
-
export type InferRecordValue<T> = T extends Record<string, infer U> ? U : any;
|
|
10
|
-
export declare function toLitObject<T extends Record<string, any>>(obj: T, accessor?: (value: InferRecordValue<T>) => string): string;
|
|
11
|
-
export type NaunceResponseAnalyzerFn = (handler: ts.ArrowFunction, deriver: TypeDeriver, node: ts.Node) => ResponseItem[];
|
|
12
|
-
export type NaunceResponseAnalyzer = Record<string, NaunceResponseAnalyzerFn>;
|
|
13
|
-
export type ResponseAnalyzerFn = (handler: ts.ArrowFunction, deriver: TypeDeriver) => ResponseItem[];
|
|
14
|
-
export declare function isEmpty(value: unknown): value is null | undefined | '';
|
|
15
|
-
export declare function pascalcase(value: string): string;
|
|
16
|
-
export declare function spinalcase(value: string): string;
|
|
17
|
-
export declare function snakecase(value: string): string;
|
|
5
|
+
export * from './lib/utils.js';
|
|
18
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,45 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
pascalcase as _pascalcase,
|
|
3
|
-
snakecase as _snakecase,
|
|
4
|
-
spinalcase as _spinalcase
|
|
5
|
-
} from "stringcase";
|
|
6
1
|
export * from "./lib/deriver.js";
|
|
7
2
|
export * from "./lib/paths.js";
|
|
8
3
|
export * from "./lib/program.js";
|
|
9
4
|
export * from "./lib/ref.js";
|
|
10
|
-
|
|
11
|
-
return [...new Map(data.map((x) => [accessor(x), x])).values()];
|
|
12
|
-
}
|
|
13
|
-
function toLitObject(obj, accessor = (value) => value) {
|
|
14
|
-
return `{${Object.keys(obj).map((key) => `${key}: ${accessor(obj[key])}`).join(", ")}}`;
|
|
15
|
-
}
|
|
16
|
-
function isEmpty(value) {
|
|
17
|
-
if (value === null || value === void 0 || value === "") {
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
if (Array.isArray(value) && value.length === 0) {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
if (typeof value === "object" && Object.keys(value).length === 0) {
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
function pascalcase(value) {
|
|
29
|
-
return _pascalcase(value.split("/").join(" "));
|
|
30
|
-
}
|
|
31
|
-
function spinalcase(value) {
|
|
32
|
-
return _spinalcase(value.split("/").join(" "));
|
|
33
|
-
}
|
|
34
|
-
function snakecase(value) {
|
|
35
|
-
return _snakecase(value.split("/").join(" "));
|
|
36
|
-
}
|
|
37
|
-
export {
|
|
38
|
-
isEmpty,
|
|
39
|
-
pascalcase,
|
|
40
|
-
removeDuplicates,
|
|
41
|
-
snakecase,
|
|
42
|
-
spinalcase,
|
|
43
|
-
toLitObject
|
|
44
|
-
};
|
|
5
|
+
export * from "./lib/utils.js";
|
|
45
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["
|
|
5
|
-
"mappings": "AAAA
|
|
4
|
+
"sourcesContent": ["export * from './lib/deriver.js';\nexport * from './lib/paths.js';\nexport * from './lib/program.js';\nexport * from './lib/ref.js';\nexport * from './lib/utils.js';\n"],
|
|
5
|
+
"mappings": "AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,18 +1,43 @@
|
|
|
1
|
-
import type { Dirent } from 'node:fs';
|
|
2
1
|
export declare function getFile(filePath: string): Promise<string | null>;
|
|
3
2
|
export declare function exist(file: string): Promise<boolean>;
|
|
4
|
-
export declare function readFolder(path: string): Promise<string[]>;
|
|
3
|
+
export declare function readFolder(path: string, recursive?: boolean): Promise<string[]>;
|
|
5
4
|
export type WriteContent = Record<string, null | string | {
|
|
6
5
|
content: string;
|
|
7
6
|
ignoreIfExists?: boolean;
|
|
8
7
|
}>;
|
|
8
|
+
export type ReadFolderFn = (folder: string) => Promise<{
|
|
9
|
+
filePath: string;
|
|
10
|
+
fileName: string;
|
|
11
|
+
isFolder: boolean;
|
|
12
|
+
content?: string;
|
|
13
|
+
}[]>;
|
|
14
|
+
export type Writer = (dir: string, contents: WriteContent) => Promise<void>;
|
|
9
15
|
export declare function writeFiles(dir: string, contents: WriteContent): Promise<void>;
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
/**
|
|
17
|
+
* @deprecated use getFolderExportsV2 instead
|
|
18
|
+
*/
|
|
19
|
+
export declare function getFolderExports(folder: string, readFolder: ReadFolderFn, includeExtension?: boolean, extensions?: string[], ignore?: (config: {
|
|
20
|
+
filePath: string;
|
|
21
|
+
fileName: string;
|
|
22
|
+
isFolder: boolean;
|
|
23
|
+
}) => boolean): Promise<string>;
|
|
24
|
+
export declare function getFolderExportsV2(folder: string, readFolder: ReadFolderFn, options?: {
|
|
12
25
|
includeExtension?: boolean;
|
|
13
26
|
extensions: string;
|
|
14
|
-
ignore?: (
|
|
27
|
+
ignore?: (fileInfo: {
|
|
28
|
+
filePath: string;
|
|
29
|
+
fileName: string;
|
|
30
|
+
isFolder: boolean;
|
|
31
|
+
}) => boolean;
|
|
15
32
|
exportSyntax: string;
|
|
16
33
|
}): Promise<string>;
|
|
17
34
|
export declare const getExt: (fileName?: string) => string;
|
|
35
|
+
export declare function addLeadingSlash(path: string): string;
|
|
36
|
+
export declare function removeTrialingSlashes(path: string, keepLastOne?: boolean): string;
|
|
37
|
+
export declare function isNullOrUndefined(value: any): value is undefined | null;
|
|
38
|
+
export declare function notNullOrUndefined<T>(value: T): value is Exclude<T, null | undefined>;
|
|
39
|
+
export declare function createWriterProxy(writer: Writer, output: string): {
|
|
40
|
+
writer: Writer;
|
|
41
|
+
files: Set<string>;
|
|
42
|
+
};
|
|
18
43
|
//# sourceMappingURL=file-system.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../../src/lib/file-system.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../../src/lib/file-system.ts"],"names":[],"mappings":"AAUA,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,0BAK7C;AAED,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAI1D;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,SAAS,UAAQ,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC,CAmBnB;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAC/B,MAAM,EACN,IAAI,GAAG,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,CAC9D,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CACpD;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,EAAE,CACJ,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE5E,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,iBAqBnE;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,YAAY,EACxB,gBAAgB,UAAO,EACvB,UAAU,WAAS,EACnB,MAAM,GAAE,CAAC,MAAM,EAAE;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CACnB,KAAK,OAAqB,mBA2B5B;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,YAAY,EACxB,OAAO,GAAE;IACP,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,OAAO,CAAC;KACnB,KAAK,OAAO,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CAMtB,mBAkCF;AAED,eAAO,MAAM,MAAM,GAAI,WAAW,MAAM,WAkBvC,CAAC;AAEF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,UAE3C;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,UAAQ,UAKtE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,SAAS,GAAG,IAAI,CAEvE;AACD,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,KAAK,EAAE,CAAC,GACP,KAAK,IAAI,OAAO,CAAC,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC,CAEvC;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,CAaxC"}
|
package/dist/lib/file-system.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
dirname,
|
|
4
|
+
extname,
|
|
5
|
+
isAbsolute,
|
|
6
|
+
join,
|
|
7
|
+
normalize,
|
|
8
|
+
relative
|
|
9
|
+
} from "node:path";
|
|
3
10
|
async function getFile(filePath) {
|
|
4
11
|
if (await exist(filePath)) {
|
|
5
12
|
return readFile(filePath, "utf-8");
|
|
@@ -9,11 +16,25 @@ async function getFile(filePath) {
|
|
|
9
16
|
async function exist(file) {
|
|
10
17
|
return stat(file).then(() => true).catch(() => false);
|
|
11
18
|
}
|
|
12
|
-
async function readFolder(path) {
|
|
13
|
-
if (await exist(path)) {
|
|
14
|
-
return
|
|
19
|
+
async function readFolder(path, recursive = false) {
|
|
20
|
+
if (!await exist(path)) {
|
|
21
|
+
return [];
|
|
15
22
|
}
|
|
16
|
-
|
|
23
|
+
const entries = await readdir(path, { withFileTypes: true });
|
|
24
|
+
const results = [];
|
|
25
|
+
for (const entry of entries) {
|
|
26
|
+
if (entry.isDirectory()) {
|
|
27
|
+
if (recursive) {
|
|
28
|
+
const subFiles = await readFolder(join(path, entry.name), true);
|
|
29
|
+
for (const sub of subFiles) {
|
|
30
|
+
results.push(`${entry.name}/${sub}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
results.push(entry.name);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return results;
|
|
17
38
|
}
|
|
18
39
|
async function writeFiles(dir, contents) {
|
|
19
40
|
await Promise.all(
|
|
@@ -37,28 +58,28 @@ async function writeFiles(dir, contents) {
|
|
|
37
58
|
})
|
|
38
59
|
);
|
|
39
60
|
}
|
|
40
|
-
async function getFolderExports(folder, includeExtension = true, extensions = ["ts"], ignore = () => false) {
|
|
41
|
-
const files = await
|
|
61
|
+
async function getFolderExports(folder, readFolder2, includeExtension = true, extensions = ["ts"], ignore = () => false) {
|
|
62
|
+
const files = await readFolder2(folder);
|
|
42
63
|
const exports = [];
|
|
43
64
|
for (const file of files) {
|
|
44
65
|
if (ignore(file)) {
|
|
45
66
|
continue;
|
|
46
67
|
}
|
|
47
|
-
if (file.
|
|
48
|
-
if (await exist(`${file.
|
|
68
|
+
if (file.isFolder) {
|
|
69
|
+
if (await exist(`${file.filePath}/index.ts`) && await readFile(`${file.filePath}/index.ts`, "utf-8") !== "") {
|
|
49
70
|
exports.push(
|
|
50
|
-
`export * from './${file.
|
|
71
|
+
`export * from './${file.fileName}/index${includeExtension ? ".ts" : ""}';`
|
|
51
72
|
);
|
|
52
73
|
}
|
|
53
|
-
} else if (file.
|
|
74
|
+
} else if (file.fileName !== "index.ts" && extensions.includes(getExt(file.fileName))) {
|
|
54
75
|
exports.push(
|
|
55
|
-
`export * from './${includeExtension ? file.
|
|
76
|
+
`export * from './${includeExtension ? file.fileName : file.fileName.replace(extname(file.fileName), "")}';`
|
|
56
77
|
);
|
|
57
78
|
}
|
|
58
79
|
}
|
|
59
80
|
return exports.join("\n");
|
|
60
81
|
}
|
|
61
|
-
async function getFolderExportsV2(folder, options = {
|
|
82
|
+
async function getFolderExportsV2(folder, readFolder2, options = {
|
|
62
83
|
extensions: "ts",
|
|
63
84
|
ignore: () => false,
|
|
64
85
|
includeExtension: true,
|
|
@@ -68,24 +89,24 @@ async function getFolderExportsV2(folder, options = {
|
|
|
68
89
|
if (!await exist(folder)) {
|
|
69
90
|
return "";
|
|
70
91
|
}
|
|
71
|
-
const files = await
|
|
92
|
+
const files = await readFolder2(folder);
|
|
72
93
|
const exports = [];
|
|
73
94
|
for (const file of files) {
|
|
74
95
|
if (options.ignore?.(file)) {
|
|
75
96
|
continue;
|
|
76
97
|
}
|
|
77
|
-
if (file.
|
|
78
|
-
if (await exist(
|
|
79
|
-
`${file.parentPath}/${file.name}/index.${options.extensions}`
|
|
80
|
-
)) {
|
|
98
|
+
if (file.isFolder) {
|
|
99
|
+
if (await exist(`${file.filePath}/index.${options.extensions}`)) {
|
|
81
100
|
exports.push(
|
|
82
|
-
`${options.exportSyntax} './${file.
|
|
101
|
+
`${options.exportSyntax} './${file.fileName}/index${options.includeExtension ? `.${options.extensions}` : ""}';`
|
|
83
102
|
);
|
|
84
103
|
}
|
|
85
|
-
} else if (file.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
104
|
+
} else if (file.fileName !== `index.${options.extensions}` && options.extensions.includes(getExt(file.fileName))) {
|
|
105
|
+
let name = options.includeExtension ? file.fileName : file.fileName.replace(extname(file.fileName), "");
|
|
106
|
+
if (name.startsWith("$")) {
|
|
107
|
+
name = `\\${name}`;
|
|
108
|
+
}
|
|
109
|
+
exports.push(`${options.exportSyntax} './${name}';`);
|
|
89
110
|
}
|
|
90
111
|
}
|
|
91
112
|
return exports.join("\n");
|
|
@@ -104,13 +125,47 @@ const getExt = (fileName) => {
|
|
|
104
125
|
}
|
|
105
126
|
return ext || "txt";
|
|
106
127
|
};
|
|
128
|
+
function addLeadingSlash(path) {
|
|
129
|
+
return normalize(join("/", path));
|
|
130
|
+
}
|
|
131
|
+
function removeTrialingSlashes(path, keepLastOne = false) {
|
|
132
|
+
while (path.endsWith("/")) {
|
|
133
|
+
path = path.slice(0, -1);
|
|
134
|
+
}
|
|
135
|
+
return path + (keepLastOne ? "/" : "");
|
|
136
|
+
}
|
|
137
|
+
function isNullOrUndefined(value) {
|
|
138
|
+
return value === void 0 || value === null;
|
|
139
|
+
}
|
|
140
|
+
function notNullOrUndefined(value) {
|
|
141
|
+
return !isNullOrUndefined(value);
|
|
142
|
+
}
|
|
143
|
+
function createWriterProxy(writer, output) {
|
|
144
|
+
const writtenFiles = /* @__PURE__ */ new Set();
|
|
145
|
+
return {
|
|
146
|
+
files: writtenFiles,
|
|
147
|
+
writer: async (dir, contents) => {
|
|
148
|
+
await writer(dir, contents);
|
|
149
|
+
for (const file of Object.keys(contents)) {
|
|
150
|
+
if (contents[file] !== null) {
|
|
151
|
+
writtenFiles.add(addLeadingSlash(`${relative(output, dir)}/${file}`));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
107
157
|
export {
|
|
158
|
+
addLeadingSlash,
|
|
159
|
+
createWriterProxy,
|
|
108
160
|
exist,
|
|
109
161
|
getExt,
|
|
110
162
|
getFile,
|
|
111
163
|
getFolderExports,
|
|
112
164
|
getFolderExportsV2,
|
|
165
|
+
isNullOrUndefined,
|
|
166
|
+
notNullOrUndefined,
|
|
113
167
|
readFolder,
|
|
168
|
+
removeTrialingSlashes,
|
|
114
169
|
writeFiles
|
|
115
170
|
};
|
|
116
171
|
//# sourceMappingURL=file-system.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/file-system.ts"],
|
|
4
|
-
"sourcesContent": ["import
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": []
|
|
4
|
+
"sourcesContent": ["import { mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises';\nimport {\n dirname,\n extname,\n isAbsolute,\n join,\n normalize,\n relative,\n} from 'node:path';\n\nexport async function getFile(filePath: string) {\n if (await exist(filePath)) {\n return readFile(filePath, 'utf-8');\n }\n return null;\n}\n\nexport async function exist(file: string): Promise<boolean> {\n return stat(file)\n .then(() => true)\n .catch(() => false);\n}\n\nexport async function readFolder(\n path: string,\n recursive = false,\n): Promise<string[]> {\n if (!(await exist(path))) {\n return [];\n }\n const entries = await readdir(path, { withFileTypes: true });\n const results: string[] = [];\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (recursive) {\n const subFiles = await readFolder(join(path, entry.name), true);\n for (const sub of subFiles) {\n results.push(`${entry.name}/${sub}`);\n }\n }\n } else {\n results.push(entry.name);\n }\n }\n return results;\n}\n\nexport type WriteContent = Record<\n string,\n null | string | { content: string; ignoreIfExists?: boolean }\n>;\n\nexport type ReadFolderFn = (folder: string) => Promise<\n {\n filePath: string;\n fileName: string;\n isFolder: boolean;\n content?: string;\n }[]\n>;\nexport type Writer = (dir: string, contents: WriteContent) => Promise<void>;\n\nexport async function writeFiles(dir: string, contents: WriteContent) {\n await Promise.all(\n Object.entries(contents).map(async ([file, content]) => {\n if (content === null) {\n return;\n }\n const filePath = isAbsolute(file) ? file : join(dir, file);\n await mkdir(dirname(filePath), { recursive: true });\n if (typeof content === 'string') {\n await writeFile(filePath, content, 'utf-8');\n } else {\n if (content.ignoreIfExists) {\n if (!(await exist(filePath))) {\n await writeFile(filePath, content.content, 'utf-8');\n }\n } else {\n await writeFile(filePath, content.content, 'utf-8');\n }\n }\n }),\n );\n}\n\n/**\n * @deprecated use getFolderExportsV2 instead\n */\nexport async function getFolderExports(\n folder: string,\n readFolder: ReadFolderFn,\n includeExtension = true,\n extensions = ['ts'],\n ignore: (config: {\n filePath: string;\n fileName: string;\n isFolder: boolean;\n }) => boolean = () => false,\n) {\n const files = await readFolder(folder);\n const exports: string[] = [];\n for (const file of files) {\n if (ignore(file)) {\n continue;\n }\n if (file.isFolder) {\n if (\n (await exist(`${file.filePath}/index.ts`)) &&\n (await readFile(`${file.filePath}/index.ts`, 'utf-8')) !== ''\n ) {\n exports.push(\n `export * from './${file.fileName}/index${includeExtension ? '.ts' : ''}';`,\n );\n }\n } else if (\n file.fileName !== 'index.ts' &&\n extensions.includes(getExt(file.fileName))\n ) {\n exports.push(\n `export * from './${includeExtension ? file.fileName : file.fileName.replace(extname(file.fileName), '')}';`,\n );\n }\n }\n return exports.join('\\n');\n}\n\nexport async function getFolderExportsV2(\n folder: string,\n readFolder: ReadFolderFn,\n options: {\n includeExtension?: boolean;\n extensions: string;\n ignore?: (fileInfo: {\n filePath: string;\n fileName: string;\n isFolder: boolean;\n }) => boolean;\n exportSyntax: string;\n } = {\n extensions: 'ts',\n ignore: () => false,\n includeExtension: true,\n exportSyntax: 'export * from ',\n },\n) {\n options.includeExtension ??= true;\n if (!(await exist(folder))) {\n return '';\n }\n const files = await readFolder(folder);\n const exports: string[] = [];\n for (const file of files) {\n if (options.ignore?.(file)) {\n continue;\n }\n if (file.isFolder) {\n if (await exist(`${file.filePath}/index.${options.extensions}`)) {\n exports.push(\n `${options.exportSyntax} './${file.fileName}/index${\n options.includeExtension ? `.${options.extensions}` : ''\n }';`,\n );\n }\n } else if (\n file.fileName !== `index.${options.extensions}` &&\n options.extensions.includes(getExt(file.fileName))\n ) {\n let name = options.includeExtension\n ? file.fileName\n : file.fileName.replace(extname(file.fileName), '');\n if (name.startsWith('$')) {\n name = `\\\\${name}`\n }\n exports.push(`${options.exportSyntax} './${name}';`);\n }\n }\n return exports.join('\\n');\n}\n\nexport const getExt = (fileName?: string) => {\n if (!fileName) {\n return ''; // shouldn't happen as there will always be a file name\n }\n const lastDot = fileName.lastIndexOf('.');\n if (lastDot === -1) {\n return '';\n }\n const ext = fileName\n .slice(lastDot + 1)\n .split('/')\n .filter(Boolean)\n .join('');\n if (ext === fileName) {\n // files that have no extension\n return '';\n }\n return ext || 'txt';\n};\n\nexport function addLeadingSlash(path: string) {\n return normalize(join('/', path));\n}\n\nexport function removeTrialingSlashes(path: string, keepLastOne = false) {\n while (path.endsWith('/')) {\n path = path.slice(0, -1);\n }\n return path + (keepLastOne ? '/' : '');\n}\n\nexport function isNullOrUndefined(value: any): value is undefined | null {\n return value === undefined || value === null;\n}\nexport function notNullOrUndefined<T>(\n value: T,\n): value is Exclude<T, null | undefined> {\n return !isNullOrUndefined(value);\n}\n\nexport function createWriterProxy(\n writer: Writer,\n output: string,\n): { writer: Writer; files: Set<string> } {\n const writtenFiles = new Set<string>();\n return {\n files: writtenFiles,\n writer: async (dir: string, contents: WriteContent) => {\n await writer(dir, contents);\n for (const file of Object.keys(contents)) {\n if (contents[file] !== null) {\n writtenFiles.add(addLeadingSlash(`${relative(output, dir)}/${file}`));\n }\n }\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["readFolder"]
|
|
7
7
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-system.test.d.ts","sourceRoot":"","sources":["../../src/lib/file-system.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { after, before, describe, it } from "node:test";
|
|
6
|
+
import {
|
|
7
|
+
addLeadingSlash,
|
|
8
|
+
exist,
|
|
9
|
+
getExt,
|
|
10
|
+
getFile,
|
|
11
|
+
getFolderExports,
|
|
12
|
+
isNullOrUndefined,
|
|
13
|
+
readFolder,
|
|
14
|
+
removeTrialingSlashes,
|
|
15
|
+
writeFiles
|
|
16
|
+
} from "./file-system.ts";
|
|
17
|
+
describe("File System Utilities - Error-First Testing", () => {
|
|
18
|
+
let testDir;
|
|
19
|
+
let nonExistentPath;
|
|
20
|
+
before(async () => {
|
|
21
|
+
testDir = join(tmpdir(), `fs-test-${Date.now()}`);
|
|
22
|
+
await mkdir(testDir, { recursive: true });
|
|
23
|
+
nonExistentPath = join(testDir, "non-existent-directory", "deep", "path");
|
|
24
|
+
});
|
|
25
|
+
after(async () => {
|
|
26
|
+
const { rm } = await import("node:fs/promises");
|
|
27
|
+
await rm(testDir, { recursive: true, force: true });
|
|
28
|
+
});
|
|
29
|
+
describe("getFile - Attack Phase: Invalid Inputs & File System Errors", () => {
|
|
30
|
+
it("should return null for non-existent file", async () => {
|
|
31
|
+
const result = await getFile(join(testDir, "does-not-exist.txt"));
|
|
32
|
+
assert.strictEqual(result, null);
|
|
33
|
+
});
|
|
34
|
+
it("should return null for empty string path", async () => {
|
|
35
|
+
const result = await getFile("");
|
|
36
|
+
assert.strictEqual(result, null);
|
|
37
|
+
});
|
|
38
|
+
it("should throw error when trying to read a directory path instead of file", async () => {
|
|
39
|
+
const dirPath = join(testDir, "test-dir-for-getfile");
|
|
40
|
+
await mkdir(dirPath);
|
|
41
|
+
await assert.rejects(() => getFile(dirPath), {
|
|
42
|
+
code: "EISDIR"
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
it("should handle permission denied scenarios gracefully", async () => {
|
|
46
|
+
const result = await getFile("/root/protected-file.txt");
|
|
47
|
+
assert.strictEqual(result, null);
|
|
48
|
+
});
|
|
49
|
+
it("should handle malformed paths", async () => {
|
|
50
|
+
const malformedPaths = [
|
|
51
|
+
"\0invalid",
|
|
52
|
+
"file\nwith\nnewlines",
|
|
53
|
+
"file with tabs"
|
|
54
|
+
];
|
|
55
|
+
for (const path of malformedPaths) {
|
|
56
|
+
const result = await getFile(path);
|
|
57
|
+
assert.strictEqual(result, null, `Failed for path: ${path}`);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe("exist - Attack Phase: Edge Cases & Invalid Paths", () => {
|
|
62
|
+
it("should return false for non-existent paths", async () => {
|
|
63
|
+
assert.strictEqual(await exist(nonExistentPath), false);
|
|
64
|
+
});
|
|
65
|
+
it("should return false for empty string", async () => {
|
|
66
|
+
assert.strictEqual(await exist(""), false);
|
|
67
|
+
});
|
|
68
|
+
it("should handle null bytes in path gracefully", async () => {
|
|
69
|
+
assert.strictEqual(await exist("path\0with\0nulls"), false);
|
|
70
|
+
});
|
|
71
|
+
it("should handle extremely long paths", async () => {
|
|
72
|
+
const longPath = "a".repeat(1e4);
|
|
73
|
+
assert.strictEqual(await exist(longPath), false);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe("readFolder - Attack Phase: Directory Corruption & Invalid States", () => {
|
|
77
|
+
it("should return empty array for non-existent directory", async () => {
|
|
78
|
+
const result = await readFolder(nonExistentPath);
|
|
79
|
+
assert.deepStrictEqual(result, []);
|
|
80
|
+
});
|
|
81
|
+
it("should throw error for file path instead of directory", async () => {
|
|
82
|
+
const filePath = join(testDir, "test-file-for-readfolder.txt");
|
|
83
|
+
await writeFile(filePath, "content");
|
|
84
|
+
await assert.rejects(() => readFolder(filePath), {
|
|
85
|
+
code: "ENOTDIR"
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
it("should handle permission denied on directory", async () => {
|
|
89
|
+
const result = await readFolder("/root");
|
|
90
|
+
assert.deepStrictEqual(result, []);
|
|
91
|
+
});
|
|
92
|
+
it("should handle recursive reading with circular symlinks gracefully", async () => {
|
|
93
|
+
const result = await readFolder(testDir, true);
|
|
94
|
+
assert.ok(Array.isArray(result));
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
describe("writeFiles - Attack Phase: Write Failures & State Corruption", () => {
|
|
98
|
+
it("should handle null content gracefully", async () => {
|
|
99
|
+
const content = {
|
|
100
|
+
"test.txt": null,
|
|
101
|
+
"valid.txt": "content"
|
|
102
|
+
};
|
|
103
|
+
await writeFiles(testDir, content);
|
|
104
|
+
assert.strictEqual(await exist(join(testDir, "test.txt")), false);
|
|
105
|
+
assert.strictEqual(await exist(join(testDir, "valid.txt")), true);
|
|
106
|
+
});
|
|
107
|
+
it("should fail gracefully when writing to read-only directory", async () => {
|
|
108
|
+
const readOnlyPath = "/read-only-system-path";
|
|
109
|
+
const content = {
|
|
110
|
+
"test.txt": "content"
|
|
111
|
+
};
|
|
112
|
+
try {
|
|
113
|
+
await writeFiles(readOnlyPath, content);
|
|
114
|
+
assert.fail("Should have thrown an error for read-only directory");
|
|
115
|
+
} catch (error) {
|
|
116
|
+
assert.ok(error instanceof Error);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
it("should handle ignoreIfExists flag correctly when file exists", async () => {
|
|
120
|
+
const filePath = join(testDir, "existing.txt");
|
|
121
|
+
await writeFile(filePath, "original");
|
|
122
|
+
const content = {
|
|
123
|
+
"existing.txt": {
|
|
124
|
+
content: "should not overwrite",
|
|
125
|
+
ignoreIfExists: true
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
await writeFiles(testDir, content);
|
|
129
|
+
const result = await getFile(filePath);
|
|
130
|
+
assert.strictEqual(result, "original");
|
|
131
|
+
});
|
|
132
|
+
it("should create deeply nested directories", async () => {
|
|
133
|
+
const content = {
|
|
134
|
+
"deep/nested/structure/file.txt": "content"
|
|
135
|
+
};
|
|
136
|
+
await writeFiles(testDir, content);
|
|
137
|
+
const result = await getFile(
|
|
138
|
+
join(testDir, "deep/nested/structure/file.txt")
|
|
139
|
+
);
|
|
140
|
+
assert.strictEqual(result, "content");
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe("getFolderExports - Attack Phase: Invalid Folder States", () => {
|
|
144
|
+
const mockReadFolder = async (folder) => {
|
|
145
|
+
if (folder === nonExistentPath) {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
return [
|
|
149
|
+
{
|
|
150
|
+
filePath: join(folder, "valid.ts"),
|
|
151
|
+
fileName: "valid.ts",
|
|
152
|
+
isFolder: false
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
filePath: join(folder, "index.ts"),
|
|
156
|
+
fileName: "index.ts",
|
|
157
|
+
isFolder: false
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
filePath: join(folder, "subfolder"),
|
|
161
|
+
fileName: "subfolder",
|
|
162
|
+
isFolder: true
|
|
163
|
+
}
|
|
164
|
+
];
|
|
165
|
+
};
|
|
166
|
+
it("should handle non-existent folder gracefully", async () => {
|
|
167
|
+
const result = await getFolderExports(nonExistentPath, mockReadFolder);
|
|
168
|
+
assert.strictEqual(result, "");
|
|
169
|
+
});
|
|
170
|
+
it("should ignore files with unsupported extensions", async () => {
|
|
171
|
+
const mockReadFolderWithJs = async () => [
|
|
172
|
+
{ filePath: "test.js", fileName: "test.js", isFolder: false },
|
|
173
|
+
{ filePath: "test.ts", fileName: "test.ts", isFolder: false }
|
|
174
|
+
];
|
|
175
|
+
const result = await getFolderExports(
|
|
176
|
+
testDir,
|
|
177
|
+
mockReadFolderWithJs,
|
|
178
|
+
true,
|
|
179
|
+
["ts"]
|
|
180
|
+
);
|
|
181
|
+
assert.ok(!result.includes("test.js"));
|
|
182
|
+
assert.ok(result.includes("test.ts"));
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
describe("getExt - Attack Phase: Malformed File Names", () => {
|
|
186
|
+
it("should handle undefined filename", () => {
|
|
187
|
+
assert.strictEqual(getExt(void 0), "");
|
|
188
|
+
});
|
|
189
|
+
it("should handle empty string filename", () => {
|
|
190
|
+
assert.strictEqual(getExt(""), "");
|
|
191
|
+
});
|
|
192
|
+
it("should handle filename with no extension", () => {
|
|
193
|
+
assert.strictEqual(getExt("filename"), "");
|
|
194
|
+
});
|
|
195
|
+
it("should handle filename with multiple dots", () => {
|
|
196
|
+
assert.strictEqual(getExt("file.name.with.multiple.dots.ts"), "ts");
|
|
197
|
+
});
|
|
198
|
+
it("should handle filename starting with dot", () => {
|
|
199
|
+
assert.strictEqual(getExt(".hidden"), "hidden");
|
|
200
|
+
});
|
|
201
|
+
it("should handle filename with only dots", () => {
|
|
202
|
+
assert.strictEqual(getExt("..."), "txt");
|
|
203
|
+
});
|
|
204
|
+
it("should handle filename with path separators", () => {
|
|
205
|
+
assert.strictEqual(getExt("path/to/file.txt"), "txt");
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
describe("Path Utilities - Attack Phase: Malformed Paths", () => {
|
|
209
|
+
describe("addLeadingSlash", () => {
|
|
210
|
+
it("should handle empty string", () => {
|
|
211
|
+
assert.strictEqual(addLeadingSlash(""), "/");
|
|
212
|
+
});
|
|
213
|
+
it("should handle path with multiple leading slashes", () => {
|
|
214
|
+
assert.strictEqual(addLeadingSlash("///path"), "/path");
|
|
215
|
+
});
|
|
216
|
+
it("should handle path with backslashes", () => {
|
|
217
|
+
const result = addLeadingSlash("path\\to\\file");
|
|
218
|
+
assert.ok(result.startsWith("/"));
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
describe("removeTrialingSlashes", () => {
|
|
222
|
+
it("should handle empty string", () => {
|
|
223
|
+
assert.strictEqual(removeTrialingSlashes(""), "");
|
|
224
|
+
});
|
|
225
|
+
it("should handle string with only slashes", () => {
|
|
226
|
+
assert.strictEqual(removeTrialingSlashes("///"), "");
|
|
227
|
+
});
|
|
228
|
+
it("should preserve single slash when keepLastOne is true", () => {
|
|
229
|
+
assert.strictEqual(removeTrialingSlashes("path///", true), "path/");
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
describe("Type Guards - Attack Phase: Type Coercion Vulnerabilities", () => {
|
|
234
|
+
describe("isNullOrUndefined", () => {
|
|
235
|
+
it("should correctly identify falsy values that are not null/undefined", () => {
|
|
236
|
+
assert.strictEqual(isNullOrUndefined(0), false);
|
|
237
|
+
assert.strictEqual(isNullOrUndefined(""), false);
|
|
238
|
+
assert.strictEqual(isNullOrUndefined(false), false);
|
|
239
|
+
assert.strictEqual(isNullOrUndefined(NaN), false);
|
|
240
|
+
});
|
|
241
|
+
it("should correctly identify null and undefined", () => {
|
|
242
|
+
assert.strictEqual(isNullOrUndefined(null), true);
|
|
243
|
+
assert.strictEqual(isNullOrUndefined(void 0), true);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
describe("type safety verification", () => {
|
|
247
|
+
it("should handle type coercion edge cases", () => {
|
|
248
|
+
const value = null;
|
|
249
|
+
if (isNullOrUndefined(value)) {
|
|
250
|
+
assert.ok(value === null || value === void 0);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
describe("State Transition Testing - File System Operations", () => {
|
|
256
|
+
it("should maintain file system consistency during complex operations", async () => {
|
|
257
|
+
const complexContent = {
|
|
258
|
+
"folder1/file1.txt": "content1",
|
|
259
|
+
"folder1/file2.txt": null,
|
|
260
|
+
// Should not be created
|
|
261
|
+
"folder2/subfolder/file3.txt": {
|
|
262
|
+
content: "content3",
|
|
263
|
+
ignoreIfExists: false
|
|
264
|
+
},
|
|
265
|
+
"folder2/existing.txt": {
|
|
266
|
+
content: "new content",
|
|
267
|
+
ignoreIfExists: true
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
await mkdir(join(testDir, "folder2"), { recursive: true });
|
|
271
|
+
await writeFile(join(testDir, "folder2/existing.txt"), "original");
|
|
272
|
+
await writeFiles(testDir, complexContent);
|
|
273
|
+
assert.strictEqual(
|
|
274
|
+
await getFile(join(testDir, "folder1/file1.txt")),
|
|
275
|
+
"content1"
|
|
276
|
+
);
|
|
277
|
+
assert.strictEqual(
|
|
278
|
+
await exist(join(testDir, "folder1/file2.txt")),
|
|
279
|
+
false
|
|
280
|
+
);
|
|
281
|
+
assert.strictEqual(
|
|
282
|
+
await getFile(join(testDir, "folder2/subfolder/file3.txt")),
|
|
283
|
+
"content3"
|
|
284
|
+
);
|
|
285
|
+
assert.strictEqual(
|
|
286
|
+
await getFile(join(testDir, "folder2/existing.txt")),
|
|
287
|
+
"original"
|
|
288
|
+
);
|
|
289
|
+
});
|
|
290
|
+
it("should maintain directory structure consistency during recursive operations", async () => {
|
|
291
|
+
const nestedDir = join(testDir, "nested/deep/structure");
|
|
292
|
+
await mkdir(nestedDir, { recursive: true });
|
|
293
|
+
await writeFile(join(nestedDir, "file.txt"), "content");
|
|
294
|
+
await writeFile(join(testDir, "nested/file2.txt"), "content2");
|
|
295
|
+
const files = await readFolder(join(testDir, "nested"), true);
|
|
296
|
+
assert.ok(files.includes("deep/structure/file.txt"));
|
|
297
|
+
assert.ok(files.includes("file2.txt"));
|
|
298
|
+
assert.strictEqual(files.filter((f) => f.startsWith("deep/")).length, 1);
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
describe("Happy Path - Basic Functionality Confirmation", () => {
|
|
302
|
+
it("should successfully read existing file", async () => {
|
|
303
|
+
const filePath = join(testDir, "test-read.txt");
|
|
304
|
+
const content = "test content";
|
|
305
|
+
await writeFile(filePath, content);
|
|
306
|
+
const result = await getFile(filePath);
|
|
307
|
+
assert.strictEqual(result, content);
|
|
308
|
+
});
|
|
309
|
+
it("should correctly identify existing files and directories", async () => {
|
|
310
|
+
const filePath = join(testDir, "test-exist-unique.txt");
|
|
311
|
+
const dirPath = join(testDir, "test-dir-unique");
|
|
312
|
+
await writeFile(filePath, "content");
|
|
313
|
+
await mkdir(dirPath);
|
|
314
|
+
assert.strictEqual(await exist(filePath), true);
|
|
315
|
+
assert.strictEqual(await exist(dirPath), true);
|
|
316
|
+
});
|
|
317
|
+
it("should read folder contents correctly", async () => {
|
|
318
|
+
const folderPath = join(testDir, "read-test");
|
|
319
|
+
await mkdir(folderPath);
|
|
320
|
+
await writeFile(join(folderPath, "file1.txt"), "content1");
|
|
321
|
+
await writeFile(join(folderPath, "file2.txt"), "content2");
|
|
322
|
+
const files = await readFolder(folderPath);
|
|
323
|
+
assert.deepStrictEqual(files.sort(), ["file1.txt", "file2.txt"]);
|
|
324
|
+
});
|
|
325
|
+
it("should write files correctly", async () => {
|
|
326
|
+
const content = {
|
|
327
|
+
"simple.txt": "simple content",
|
|
328
|
+
"complex.txt": {
|
|
329
|
+
content: "complex content",
|
|
330
|
+
ignoreIfExists: false
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
await writeFiles(testDir, content);
|
|
334
|
+
assert.strictEqual(
|
|
335
|
+
await getFile(join(testDir, "simple.txt")),
|
|
336
|
+
"simple content"
|
|
337
|
+
);
|
|
338
|
+
assert.strictEqual(
|
|
339
|
+
await getFile(join(testDir, "complex.txt")),
|
|
340
|
+
"complex content"
|
|
341
|
+
);
|
|
342
|
+
});
|
|
343
|
+
it("should extract file extensions correctly", () => {
|
|
344
|
+
assert.strictEqual(getExt("file.txt"), "txt");
|
|
345
|
+
assert.strictEqual(getExt("script.js"), "js");
|
|
346
|
+
assert.strictEqual(getExt("component.tsx"), "tsx");
|
|
347
|
+
});
|
|
348
|
+
it("should format paths correctly", () => {
|
|
349
|
+
assert.strictEqual(addLeadingSlash("path/to/file"), "/path/to/file");
|
|
350
|
+
assert.strictEqual(
|
|
351
|
+
removeTrialingSlashes("path/to/file///"),
|
|
352
|
+
"path/to/file"
|
|
353
|
+
);
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
//# sourceMappingURL=file-system.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/lib/file-system.test.ts"],
|
|
4
|
+
"sourcesContent": ["import assert from 'node:assert/strict';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { after, before, describe, it } from 'node:test';\n\nimport {\n type WriteContent,\n addLeadingSlash,\n exist,\n getExt,\n getFile,\n getFolderExports,\n isNullOrUndefined,\n readFolder,\n removeTrialingSlashes,\n writeFiles,\n} from './file-system.ts';\n\ndescribe('File System Utilities - Error-First Testing', () => {\n let testDir: string;\n let nonExistentPath: string;\n\n before(async () => {\n testDir = join(tmpdir(), `fs-test-${Date.now()}`);\n await mkdir(testDir, { recursive: true });\n nonExistentPath = join(testDir, 'non-existent-directory', 'deep', 'path');\n });\n\n after(async () => {\n const { rm } = await import('node:fs/promises');\n await rm(testDir, { recursive: true, force: true });\n });\n\n describe('getFile - Attack Phase: Invalid Inputs & File System Errors', () => {\n it('should return null for non-existent file', async () => {\n const result = await getFile(join(testDir, 'does-not-exist.txt'));\n assert.strictEqual(result, null);\n });\n\n it('should return null for empty string path', async () => {\n const result = await getFile('');\n assert.strictEqual(result, null);\n });\n\n it('should throw error when trying to read a directory path instead of file', async () => {\n const dirPath = join(testDir, 'test-dir-for-getfile');\n await mkdir(dirPath);\n\n await assert.rejects(() => getFile(dirPath), {\n code: 'EISDIR',\n });\n });\n\n it('should handle permission denied scenarios gracefully', async () => {\n // Test with invalid path that would cause permission errors\n const result = await getFile('/root/protected-file.txt');\n assert.strictEqual(result, null);\n });\n\n it('should handle malformed paths', async () => {\n const malformedPaths = [\n '\\0invalid',\n 'file\\nwith\\nnewlines',\n 'file\\twith\\ttabs',\n ];\n\n for (const path of malformedPaths) {\n const result = await getFile(path);\n assert.strictEqual(result, null, `Failed for path: ${path}`);\n }\n });\n });\n\n describe('exist - Attack Phase: Edge Cases & Invalid Paths', () => {\n it('should return false for non-existent paths', async () => {\n assert.strictEqual(await exist(nonExistentPath), false);\n });\n\n it('should return false for empty string', async () => {\n assert.strictEqual(await exist(''), false);\n });\n\n it('should handle null bytes in path gracefully', async () => {\n assert.strictEqual(await exist('path\\0with\\0nulls'), false);\n });\n\n it('should handle extremely long paths', async () => {\n const longPath = 'a'.repeat(10000);\n assert.strictEqual(await exist(longPath), false);\n });\n });\n\n describe('readFolder - Attack Phase: Directory Corruption & Invalid States', () => {\n it('should return empty array for non-existent directory', async () => {\n const result = await readFolder(nonExistentPath);\n assert.deepStrictEqual(result, []);\n });\n\n it('should throw error for file path instead of directory', async () => {\n const filePath = join(testDir, 'test-file-for-readfolder.txt');\n await writeFile(filePath, 'content');\n\n // readFolder should throw an error when given a file path\n await assert.rejects(() => readFolder(filePath), {\n code: 'ENOTDIR',\n });\n });\n\n it('should handle permission denied on directory', async () => {\n const result = await readFolder('/root');\n assert.deepStrictEqual(result, []);\n });\n\n it('should handle recursive reading with circular symlinks gracefully', async () => {\n // This tests the function's resilience to infinite loops\n const result = await readFolder(testDir, true);\n assert.ok(Array.isArray(result));\n });\n });\n\n describe('writeFiles - Attack Phase: Write Failures & State Corruption', () => {\n it('should handle null content gracefully', async () => {\n const content: WriteContent = {\n 'test.txt': null,\n 'valid.txt': 'content',\n };\n\n await writeFiles(testDir, content);\n\n // Verify null content file was not created\n assert.strictEqual(await exist(join(testDir, 'test.txt')), false);\n // Verify valid file was created\n assert.strictEqual(await exist(join(testDir, 'valid.txt')), true);\n });\n\n it('should fail gracefully when writing to read-only directory', async () => {\n const readOnlyPath = '/read-only-system-path';\n const content: WriteContent = {\n 'test.txt': 'content',\n };\n\n try {\n await writeFiles(readOnlyPath, content);\n assert.fail('Should have thrown an error for read-only directory');\n } catch (error) {\n assert.ok(error instanceof Error);\n }\n });\n\n it('should handle ignoreIfExists flag correctly when file exists', async () => {\n const filePath = join(testDir, 'existing.txt');\n await writeFile(filePath, 'original');\n\n const content: WriteContent = {\n 'existing.txt': {\n content: 'should not overwrite',\n ignoreIfExists: true,\n },\n };\n\n await writeFiles(testDir, content);\n const result = await getFile(filePath);\n assert.strictEqual(result, 'original');\n });\n\n it('should create deeply nested directories', async () => {\n const content: WriteContent = {\n 'deep/nested/structure/file.txt': 'content',\n };\n\n await writeFiles(testDir, content);\n const result = await getFile(\n join(testDir, 'deep/nested/structure/file.txt'),\n );\n assert.strictEqual(result, 'content');\n });\n });\n\n describe('getFolderExports - Attack Phase: Invalid Folder States', () => {\n const mockReadFolder = async (folder: string) => {\n if (folder === nonExistentPath) {\n return [];\n }\n return [\n {\n filePath: join(folder, 'valid.ts'),\n fileName: 'valid.ts',\n isFolder: false,\n },\n {\n filePath: join(folder, 'index.ts'),\n fileName: 'index.ts',\n isFolder: false,\n },\n {\n filePath: join(folder, 'subfolder'),\n fileName: 'subfolder',\n isFolder: true,\n },\n ];\n };\n\n it('should handle non-existent folder gracefully', async () => {\n const result = await getFolderExports(nonExistentPath, mockReadFolder);\n assert.strictEqual(result, '');\n });\n\n it('should ignore files with unsupported extensions', async () => {\n const mockReadFolderWithJs = async () => [\n { filePath: 'test.js', fileName: 'test.js', isFolder: false },\n { filePath: 'test.ts', fileName: 'test.ts', isFolder: false },\n ];\n\n const result = await getFolderExports(\n testDir,\n mockReadFolderWithJs,\n true,\n ['ts'],\n );\n assert.ok(!result.includes('test.js'));\n assert.ok(result.includes('test.ts'));\n });\n });\n\n describe('getExt - Attack Phase: Malformed File Names', () => {\n it('should handle undefined filename', () => {\n assert.strictEqual(getExt(undefined), '');\n });\n\n it('should handle empty string filename', () => {\n assert.strictEqual(getExt(''), '');\n });\n\n it('should handle filename with no extension', () => {\n assert.strictEqual(getExt('filename'), '');\n });\n\n it('should handle filename with multiple dots', () => {\n assert.strictEqual(getExt('file.name.with.multiple.dots.ts'), 'ts');\n });\n\n it('should handle filename starting with dot', () => {\n // Based on the actual implementation, '.hidden' should return 'hidden'\n assert.strictEqual(getExt('.hidden'), 'hidden');\n });\n\n it('should handle filename with only dots', () => {\n // Based on the actual implementation, '...' returns 'txt' as fallback\n assert.strictEqual(getExt('...'), 'txt');\n });\n\n it('should handle filename with path separators', () => {\n assert.strictEqual(getExt('path/to/file.txt'), 'txt');\n });\n });\n\n describe('Path Utilities - Attack Phase: Malformed Paths', () => {\n describe('addLeadingSlash', () => {\n it('should handle empty string', () => {\n assert.strictEqual(addLeadingSlash(''), '/');\n });\n\n it('should handle path with multiple leading slashes', () => {\n assert.strictEqual(addLeadingSlash('///path'), '/path');\n });\n\n it('should handle path with backslashes', () => {\n const result = addLeadingSlash('path\\\\to\\\\file');\n assert.ok(result.startsWith('/'));\n });\n });\n\n describe('removeTrialingSlashes', () => {\n it('should handle empty string', () => {\n assert.strictEqual(removeTrialingSlashes(''), '');\n });\n\n it('should handle string with only slashes', () => {\n assert.strictEqual(removeTrialingSlashes('///'), '');\n });\n\n it('should preserve single slash when keepLastOne is true', () => {\n assert.strictEqual(removeTrialingSlashes('path///', true), 'path/');\n });\n });\n });\n\n describe('Type Guards - Attack Phase: Type Coercion Vulnerabilities', () => {\n describe('isNullOrUndefined', () => {\n it('should correctly identify falsy values that are not null/undefined', () => {\n assert.strictEqual(isNullOrUndefined(0), false);\n assert.strictEqual(isNullOrUndefined(''), false);\n assert.strictEqual(isNullOrUndefined(false), false);\n assert.strictEqual(isNullOrUndefined(NaN), false);\n });\n\n it('should correctly identify null and undefined', () => {\n assert.strictEqual(isNullOrUndefined(null), true);\n assert.strictEqual(isNullOrUndefined(undefined), true);\n });\n });\n\n // Remove notNullOrUndefined tests since it doesn't exist in the source\n describe('type safety verification', () => {\n it('should handle type coercion edge cases', () => {\n // Test that isNullOrUndefined works as a proper type guard\n const value: unknown = null;\n if (isNullOrUndefined(value)) {\n // TypeScript should narrow the type here\n assert.ok(value === null || value === undefined);\n }\n });\n });\n });\n\n describe('State Transition Testing - File System Operations', () => {\n it('should maintain file system consistency during complex operations', async () => {\n const complexContent: WriteContent = {\n 'folder1/file1.txt': 'content1',\n 'folder1/file2.txt': null, // Should not be created\n 'folder2/subfolder/file3.txt': {\n content: 'content3',\n ignoreIfExists: false,\n },\n 'folder2/existing.txt': {\n content: 'new content',\n ignoreIfExists: true,\n },\n };\n\n // Pre-create existing file\n await mkdir(join(testDir, 'folder2'), { recursive: true });\n await writeFile(join(testDir, 'folder2/existing.txt'), 'original');\n\n await writeFiles(testDir, complexContent);\n\n // Verify final state\n assert.strictEqual(\n await getFile(join(testDir, 'folder1/file1.txt')),\n 'content1',\n );\n assert.strictEqual(\n await exist(join(testDir, 'folder1/file2.txt')),\n false,\n );\n assert.strictEqual(\n await getFile(join(testDir, 'folder2/subfolder/file3.txt')),\n 'content3',\n );\n assert.strictEqual(\n await getFile(join(testDir, 'folder2/existing.txt')),\n 'original',\n );\n });\n\n it('should maintain directory structure consistency during recursive operations', async () => {\n // Create nested structure\n const nestedDir = join(testDir, 'nested/deep/structure');\n await mkdir(nestedDir, { recursive: true });\n await writeFile(join(nestedDir, 'file.txt'), 'content');\n await writeFile(join(testDir, 'nested/file2.txt'), 'content2');\n\n const files = await readFolder(join(testDir, 'nested'), true);\n\n // Verify structure is maintained\n assert.ok(files.includes('deep/structure/file.txt'));\n assert.ok(files.includes('file2.txt'));\n assert.strictEqual(files.filter((f) => f.startsWith('deep/')).length, 1);\n });\n });\n\n describe('Happy Path - Basic Functionality Confirmation', () => {\n it('should successfully read existing file', async () => {\n const filePath = join(testDir, 'test-read.txt');\n const content = 'test content';\n await writeFile(filePath, content);\n\n const result = await getFile(filePath);\n assert.strictEqual(result, content);\n });\n\n it('should correctly identify existing files and directories', async () => {\n const filePath = join(testDir, 'test-exist-unique.txt');\n const dirPath = join(testDir, 'test-dir-unique');\n\n await writeFile(filePath, 'content');\n await mkdir(dirPath);\n\n assert.strictEqual(await exist(filePath), true);\n assert.strictEqual(await exist(dirPath), true);\n });\n\n it('should read folder contents correctly', async () => {\n const folderPath = join(testDir, 'read-test');\n await mkdir(folderPath);\n await writeFile(join(folderPath, 'file1.txt'), 'content1');\n await writeFile(join(folderPath, 'file2.txt'), 'content2');\n\n const files = await readFolder(folderPath);\n assert.deepStrictEqual(files.sort(), ['file1.txt', 'file2.txt']);\n });\n\n it('should write files correctly', async () => {\n const content: WriteContent = {\n 'simple.txt': 'simple content',\n 'complex.txt': {\n content: 'complex content',\n ignoreIfExists: false,\n },\n };\n\n await writeFiles(testDir, content);\n\n assert.strictEqual(\n await getFile(join(testDir, 'simple.txt')),\n 'simple content',\n );\n assert.strictEqual(\n await getFile(join(testDir, 'complex.txt')),\n 'complex content',\n );\n });\n\n it('should extract file extensions correctly', () => {\n assert.strictEqual(getExt('file.txt'), 'txt');\n assert.strictEqual(getExt('script.js'), 'js');\n assert.strictEqual(getExt('component.tsx'), 'tsx');\n });\n\n it('should format paths correctly', () => {\n assert.strictEqual(addLeadingSlash('path/to/file'), '/path/to/file');\n assert.strictEqual(\n removeTrialingSlashes('path/to/file///'),\n 'path/to/file',\n );\n });\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/lib/paths.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { OperationObject, PathsObject } from 'openapi3-ts/oas31';
|
|
2
2
|
import { $types } from './deriver.js';
|
|
3
|
+
export type InjectImport = {
|
|
4
|
+
import: string;
|
|
5
|
+
from: string;
|
|
6
|
+
};
|
|
3
7
|
export type Method = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'trace' | 'head';
|
|
4
8
|
export declare const methods: readonly ["get", "post", "put", "patch", "delete", "trace", "head"];
|
|
5
9
|
export type SemanticSource = 'query' | 'queries' | 'body' | 'params' | 'headers';
|
|
@@ -21,7 +25,7 @@ export type OnOperation = (sourceFile: string, method: Method, path: string, ope
|
|
|
21
25
|
export declare class Paths {
|
|
22
26
|
#private;
|
|
23
27
|
constructor(config: {
|
|
24
|
-
|
|
28
|
+
imports: InjectImport[];
|
|
25
29
|
onOperation?: OnOperation;
|
|
26
30
|
});
|
|
27
31
|
addPath(name: string, path: string, method: Method, contentType: string | undefined, selectors: Selector[], responses: ResponseItem[], sourceFile: string, tags?: string[], description?: string): this;
|
package/dist/lib/paths.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,eAAe,EAEf,WAAW,EAIZ,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,MAAM,MAAM,MAAM,GACd,KAAK,GACL,MAAM,GACN,KAAK,GACL,OAAO,GACP,QAAQ,GACR,OAAO,GACP,MAAM,CAAC;AACX,eAAO,MAAM,OAAO,qEAQV,CAAC;AACX,MAAM,MAAM,cAAc,GACtB,OAAO,GACP,SAAS,GACT,MAAM,GACN,QAAQ,GACR,SAAS,CAAC;AAQd,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC;CAChD;AAED,MAAM,MAAM,WAAW,GAAG,CACxB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,eAAe,KACvB,WAAW,CAAC;AACjB,qBAAa,KAAK;;gBAeJ,MAAM,EAAE;QAAE,
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,eAAe,EAEf,WAAW,EAIZ,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AACF,MAAM,MAAM,MAAM,GACd,KAAK,GACL,MAAM,GACN,KAAK,GACL,OAAO,GACP,QAAQ,GACR,OAAO,GACP,MAAM,CAAC;AACX,eAAO,MAAM,OAAO,qEAQV,CAAC;AACX,MAAM,MAAM,cAAc,GACtB,OAAO,GACP,SAAS,GACT,MAAM,GACN,QAAQ,GACR,SAAS,CAAC;AAQd,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC;CAChD;AAED,MAAM,MAAM,WAAW,GAAG,CACxB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,eAAe,KACvB,WAAW,CAAC;AACjB,qBAAa,KAAK;;gBAeJ,MAAM,EAAE;QAAE,OAAO,EAAE,YAAY,EAAE,CAAC;QAAC,WAAW,CAAC,EAAE,WAAW,CAAA;KAAE;IAK1E,OAAO,CACL,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,SAAS,EAAE,QAAQ,EAAE,EACrB,SAAS,EAAE,YAAY,EAAE,EACzB,UAAU,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE,MAAM,EAAE,EACf,WAAW,CAAC,EAAE,MAAM;IA2GhB,QAAQ;CA6Df;AA4BD,UAAU,QAAQ;IAChB,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,GAAG,CAyCxE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,MAAM,CAEzD"}
|
package/dist/lib/paths.js
CHANGED
|
@@ -15,11 +15,11 @@ const semanticSourceToOpenAPI = {
|
|
|
15
15
|
params: "path"
|
|
16
16
|
};
|
|
17
17
|
class Paths {
|
|
18
|
-
#
|
|
18
|
+
#imports = [];
|
|
19
19
|
#onOperation;
|
|
20
20
|
#operations = [];
|
|
21
21
|
constructor(config) {
|
|
22
|
-
this.#
|
|
22
|
+
this.#imports = config.imports;
|
|
23
23
|
this.#onOperation = config.onOperation;
|
|
24
24
|
}
|
|
25
25
|
addPath(name, path, method, contentType, selectors, responses, sourceFile, tags, description) {
|
|
@@ -94,7 +94,7 @@ class Paths {
|
|
|
94
94
|
if (selector.source === "body") {
|
|
95
95
|
bodySchemaProps[selector.name] = {
|
|
96
96
|
required: selector.required,
|
|
97
|
-
schema: await evalZod(selector.against, this.#
|
|
97
|
+
schema: await evalZod(selector.against, this.#imports)
|
|
98
98
|
};
|
|
99
99
|
continue;
|
|
100
100
|
}
|
|
@@ -102,7 +102,7 @@ class Paths {
|
|
|
102
102
|
in: semanticSourceToOpenAPI[selector.source],
|
|
103
103
|
name: selector.name,
|
|
104
104
|
required: selector.required,
|
|
105
|
-
schema: await evalZod(selector.against, this.#
|
|
105
|
+
schema: await evalZod(selector.against, this.#imports)
|
|
106
106
|
};
|
|
107
107
|
parameters.push(parameter);
|
|
108
108
|
}
|
|
@@ -163,13 +163,13 @@ class Paths {
|
|
|
163
163
|
return path.replace(/:([^/]+)/g, "{$1}");
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
|
-
async function evalZod(schema,
|
|
166
|
+
async function evalZod(schema, imports = []) {
|
|
167
167
|
const lines = [
|
|
168
168
|
`import { createRequire } from "node:module";`,
|
|
169
169
|
`const filename = "${import.meta.url}";`,
|
|
170
170
|
`const require = createRequire(filename);`,
|
|
171
171
|
`const z = require("zod");`,
|
|
172
|
-
|
|
172
|
+
...imports.map((imp) => `const ${imp.import} = require('${imp.from}');`),
|
|
173
173
|
`const {zodToJsonSchema} = require('zod-to-json-schema');`,
|
|
174
174
|
`const schema = ${schema.replace(".optional()", "").replaceAll("instanceof(File)", "string().base64()")};`,
|
|
175
175
|
`const jsonSchema = zodToJsonSchema(schema, {
|
package/dist/lib/paths.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/paths.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n HeadersObject,\n OperationObject,\n ParameterObject,\n PathsObject,\n ResponseObject,\n ResponsesObject,\n SchemaObject,\n} from 'openapi3-ts/oas31';\n\nimport { $types } from './deriver.js';\n\nexport type Method =\n | 'get'\n | 'post'\n | 'put'\n | 'patch'\n | 'delete'\n | 'trace'\n | 'head';\nexport const methods = [\n 'get',\n 'post',\n 'put',\n 'patch',\n 'delete',\n 'trace',\n 'head',\n] as const;\nexport type SemanticSource =\n | 'query'\n | 'queries'\n | 'body'\n | 'params'\n | 'headers';\n\nconst semanticSourceToOpenAPI = {\n queries: 'query',\n query: 'query',\n headers: 'header',\n params: 'path',\n} as const;\nexport interface Selector {\n name: string;\n select: string;\n against: string;\n source: SemanticSource;\n nullable: boolean;\n required: boolean;\n}\n\nexport interface ResponseItem {\n statusCode: string;\n response?: DateType;\n contentType: string;\n headers: (string | Record<string, string[]>)[];\n}\n\nexport type OnOperation = (\n sourceFile: string,\n method: Method,\n path: string,\n operation: OperationObject,\n) => PathsObject;\nexport class Paths {\n #commonZodImport?: string;\n #onOperation?: OnOperation;\n #operations: Array<{\n sourceFile: string;\n name: string;\n path: string;\n method: Method;\n selectors: Selector[];\n responses: ResponsesObject;\n contentType?: string;\n tags?: string[];\n description?: string;\n }> = [];\n\n constructor(config: { commonZodImport?: string; onOperation?: OnOperation }) {\n this.#commonZodImport = config.commonZodImport;\n this.#onOperation = config.onOperation;\n }\n\n addPath(\n name: string,\n path: string,\n method: Method,\n contentType: string | undefined,\n selectors: Selector[],\n responses: ResponseItem[],\n sourceFile: string,\n tags?: string[],\n description?: string,\n ) {\n const responsesObject = this.#responseItemToResponses(responses);\n\n this.#operations.push({\n name,\n path: this.#tunePath(path),\n sourceFile,\n contentType: contentType,\n method,\n selectors,\n responses: responsesObject,\n tags,\n description,\n });\n return this;\n }\n\n #responseItemToResponses(responses: ResponseItem[]): ResponsesObject {\n const responsesObject: ResponsesObject = {};\n for (const item of responses) {\n const ct = item.contentType;\n const schema = item.response ? toSchema(item.response) : {};\n if (!responsesObject[item.statusCode]) {\n responsesObject[item.statusCode] = {\n description: `Response for ${item.statusCode}`,\n content:\n ct !== 'empty'\n ? {\n [ct]:\n ct === 'application/octet-stream'\n ? { schema: { type: 'string', format: 'binary' } }\n : { schema },\n }\n : undefined,\n headers: item.headers.length\n ? item.headers.reduce<HeadersObject>((acc, current) => {\n const headers =\n typeof current === 'string' ? { [current]: [] } : current;\n return Object.entries(headers).reduce<HeadersObject>(\n (subAcc, [key, value]) => {\n const header: HeadersObject = {\n [key]: {\n schema: {\n type: 'string',\n enum: value.length ? value : undefined,\n },\n },\n };\n return { ...subAcc, ...header };\n },\n acc,\n );\n }, {})\n : undefined,\n } satisfies ResponseObject;\n } else {\n if (!responsesObject[item.statusCode].content[ct]) {\n responsesObject[item.statusCode].content[ct] = { schema };\n } else {\n const existing = responsesObject[item.statusCode].content[ct]\n .schema as SchemaObject;\n if (existing.oneOf) {\n if (\n !existing.oneOf.find(\n (it) => JSON.stringify(it) === JSON.stringify(schema),\n )\n ) {\n existing.oneOf.push(schema);\n }\n } else if (JSON.stringify(existing) !== JSON.stringify(schema)) {\n responsesObject[item.statusCode].content[ct].schema = {\n oneOf: [existing, schema],\n };\n }\n }\n }\n }\n return responsesObject;\n }\n\n async #selectosToParameters(selectors: Selector[]) {\n const parameters: ParameterObject[] = [];\n const bodySchemaProps: Record<\n string,\n { required: boolean; schema: SchemaObject }\n > = {};\n for (const selector of selectors) {\n if (selector.source === 'body') {\n bodySchemaProps[selector.name] = {\n required: selector.required,\n schema: await evalZod(selector.against, this.#commonZodImport),\n };\n continue;\n }\n\n const parameter: ParameterObject = {\n in: semanticSourceToOpenAPI[selector.source],\n name: selector.name,\n required: selector.required,\n schema: await evalZod(selector.against, this.#commonZodImport),\n };\n parameters.push(parameter);\n }\n return { parameters, bodySchemaProps };\n }\n\n async getPaths() {\n const operations: PathsObject = {};\n for (const operation of this.#operations) {\n const { path, method, selectors } = operation;\n const { parameters, bodySchemaProps } =\n await this.#selectosToParameters(selectors);\n const bodySchema: Record<string, SchemaObject> = {};\n const required: string[] = [];\n for (const [key, value] of Object.entries(bodySchemaProps)) {\n if (value.required) {\n required.push(key);\n }\n bodySchema[key] = value.schema;\n }\n const operationObject: OperationObject = {\n operationId: operation.name,\n parameters,\n tags: operation.tags,\n description: operation.description,\n requestBody: Object.keys(bodySchema).length\n ? {\n required: required.length ? true : false,\n content: {\n [operation.contentType || 'application/json']: {\n schema: {\n required: required.length ? required : undefined,\n type: 'object',\n properties: bodySchema,\n },\n },\n },\n }\n : undefined,\n responses:\n Object.keys(operation.responses).length === 0\n ? undefined\n : operation.responses,\n };\n if (!operations[path]) {\n operations[path] = {};\n }\n operations[path][method] = operationObject;\n if (this.#onOperation) {\n const paths = this.#onOperation?.(\n operation.sourceFile,\n method,\n path,\n operationObject,\n );\n Object.assign(operations, paths ?? {});\n }\n }\n return operations;\n }\n\n /**\n * Converts Express/Node.js style path parameters (/path/:param) to OpenAPI style (/path/{param})\n */\n #tunePath(path: string): string {\n return path.replace(/:([^/]+)/g, '{$1}');\n }\n}\n\nasync function evalZod(schema: string, commonZodImport?: string) {\n // https://github.com/nodejs/node/issues/51956\n const lines = [\n `import { createRequire } from \"node:module\";`,\n `const filename = \"${import.meta.url}\";`,\n `const require = createRequire(filename);`,\n `const z = require(\"zod\");`,\n commonZodImport ? `const commonZod = require('${commonZodImport}');` : '',\n `const {zodToJsonSchema} = require('zod-to-json-schema');`,\n `const schema = ${schema.replace('.optional()', '').replaceAll('instanceof(File)', 'string().base64()')};`,\n `const jsonSchema = zodToJsonSchema(schema, {\n $refStrategy: 'root',\n basePath: ['#', 'components', 'schemas'],\n target: 'jsonSchema7',\n base64Strategy: 'format:binary',\n });`,\n `export default jsonSchema;`,\n ];\n\n const base64 = Buffer.from(lines.join('\\n')).toString('base64');\n return import(`data:text/javascript;base64,${base64}`)\n .then((mod) => mod.default)\n .then(({ $schema, ...result }) => result);\n}\n\ninterface DateType {\n [$types]: any[];\n kind: string;\n optional: boolean;\n value?: string;\n}\n\nexport function toSchema(data: DateType | string | null | undefined): any {\n if (data === null || data === undefined) {\n return { type: 'any' };\n } else if (typeof data === 'string') {\n const isRef = data.startsWith('#');\n if (isRef) {\n return { $ref: data };\n }\n return { type: data };\n } else if (data.kind === 'literal') {\n return { enum: [data.value], type: data[$types][0] };\n } else if (data.kind === 'record') {\n return {\n type: 'object',\n additionalProperties: toSchema(data[$types][0]),\n };\n } else if (data.kind === 'array') {\n const items = data[$types].map(toSchema);\n return { type: 'array', items: data[$types].length ? items[0] : {} };\n } else if (data.kind === 'union') {\n return { anyOf: data[$types].map(toSchema) };\n } else if (data.kind === 'intersection') {\n return { allOf: data[$types].map(toSchema) };\n } else if ($types in data) {\n return data[$types].map(toSchema)[0] ?? {};\n } else {\n const props: Record<string, unknown> = {};\n const required: string[] = [];\n for (const [key, value] of Object.entries(data)) {\n props[key] = toSchema(value as any);\n if (!(value as any).optional) {\n required.push(key);\n }\n }\n return {\n type: 'object',\n properties: props,\n required,\n additionalProperties: false,\n };\n }\n}\n\nexport function isHttpMethod(name: string): name is Method {\n return ['get', 'post', 'put', 'delete', 'patch'].includes(name);\n}\n"],
|
|
5
|
-
"mappings": "AAUA,SAAS,cAAc;
|
|
4
|
+
"sourcesContent": ["import type {\n HeadersObject,\n OperationObject,\n ParameterObject,\n PathsObject,\n ResponseObject,\n ResponsesObject,\n SchemaObject,\n} from 'openapi3-ts/oas31';\n\nimport { $types } from './deriver.js';\n\nexport type InjectImport = {\n import: string;\n from: string;\n};\nexport type Method =\n | 'get'\n | 'post'\n | 'put'\n | 'patch'\n | 'delete'\n | 'trace'\n | 'head';\nexport const methods = [\n 'get',\n 'post',\n 'put',\n 'patch',\n 'delete',\n 'trace',\n 'head',\n] as const;\nexport type SemanticSource =\n | 'query'\n | 'queries'\n | 'body'\n | 'params'\n | 'headers';\n\nconst semanticSourceToOpenAPI = {\n queries: 'query',\n query: 'query',\n headers: 'header',\n params: 'path',\n} as const;\nexport interface Selector {\n name: string;\n select: string;\n against: string;\n source: SemanticSource;\n nullable: boolean;\n required: boolean;\n}\n\nexport interface ResponseItem {\n statusCode: string;\n response?: DateType;\n contentType: string;\n headers: (string | Record<string, string[]>)[];\n}\n\nexport type OnOperation = (\n sourceFile: string,\n method: Method,\n path: string,\n operation: OperationObject,\n) => PathsObject;\nexport class Paths {\n #imports: InjectImport[] = [];\n #onOperation?: OnOperation;\n #operations: Array<{\n sourceFile: string;\n name: string;\n path: string;\n method: Method;\n selectors: Selector[];\n responses: ResponsesObject;\n contentType?: string;\n tags?: string[];\n description?: string;\n }> = [];\n\n constructor(config: { imports: InjectImport[]; onOperation?: OnOperation }) {\n this.#imports = config.imports;\n this.#onOperation = config.onOperation;\n }\n\n addPath(\n name: string,\n path: string,\n method: Method,\n contentType: string | undefined,\n selectors: Selector[],\n responses: ResponseItem[],\n sourceFile: string,\n tags?: string[],\n description?: string,\n ) {\n const responsesObject = this.#responseItemToResponses(responses);\n\n this.#operations.push({\n name,\n path: this.#tunePath(path),\n sourceFile,\n contentType: contentType,\n method,\n selectors,\n responses: responsesObject,\n tags,\n description,\n });\n return this;\n }\n\n #responseItemToResponses(responses: ResponseItem[]): ResponsesObject {\n const responsesObject: ResponsesObject = {};\n for (const item of responses) {\n const ct = item.contentType;\n const schema = item.response ? toSchema(item.response) : {};\n if (!responsesObject[item.statusCode]) {\n responsesObject[item.statusCode] = {\n description: `Response for ${item.statusCode}`,\n content:\n ct !== 'empty'\n ? {\n [ct]:\n ct === 'application/octet-stream'\n ? { schema: { type: 'string', format: 'binary' } }\n : { schema },\n }\n : undefined,\n headers: item.headers.length\n ? item.headers.reduce<HeadersObject>((acc, current) => {\n const headers =\n typeof current === 'string' ? { [current]: [] } : current;\n return Object.entries(headers).reduce<HeadersObject>(\n (subAcc, [key, value]) => {\n const header: HeadersObject = {\n [key]: {\n schema: {\n type: 'string',\n enum: value.length ? value : undefined,\n },\n },\n };\n return { ...subAcc, ...header };\n },\n acc,\n );\n }, {})\n : undefined,\n } satisfies ResponseObject;\n } else {\n if (!responsesObject[item.statusCode].content[ct]) {\n responsesObject[item.statusCode].content[ct] = { schema };\n } else {\n const existing = responsesObject[item.statusCode].content[ct]\n .schema as SchemaObject;\n if (existing.oneOf) {\n if (\n !existing.oneOf.find(\n (it) => JSON.stringify(it) === JSON.stringify(schema),\n )\n ) {\n existing.oneOf.push(schema);\n }\n } else if (JSON.stringify(existing) !== JSON.stringify(schema)) {\n responsesObject[item.statusCode].content[ct].schema = {\n oneOf: [existing, schema],\n };\n }\n }\n }\n }\n return responsesObject;\n }\n\n async #selectosToParameters(selectors: Selector[]) {\n const parameters: ParameterObject[] = [];\n const bodySchemaProps: Record<\n string,\n { required: boolean; schema: SchemaObject }\n > = {};\n for (const selector of selectors) {\n if (selector.source === 'body') {\n bodySchemaProps[selector.name] = {\n required: selector.required,\n schema: await evalZod(selector.against, this.#imports),\n };\n continue;\n }\n\n const parameter: ParameterObject = {\n in: semanticSourceToOpenAPI[selector.source],\n name: selector.name,\n required: selector.required,\n schema: await evalZod(selector.against, this.#imports),\n };\n parameters.push(parameter);\n }\n return { parameters, bodySchemaProps };\n }\n\n async getPaths() {\n const operations: PathsObject = {};\n for (const operation of this.#operations) {\n const { path, method, selectors } = operation;\n const { parameters, bodySchemaProps } =\n await this.#selectosToParameters(selectors);\n const bodySchema: Record<string, SchemaObject> = {};\n const required: string[] = [];\n for (const [key, value] of Object.entries(bodySchemaProps)) {\n if (value.required) {\n required.push(key);\n }\n bodySchema[key] = value.schema;\n }\n const operationObject: OperationObject = {\n operationId: operation.name,\n parameters,\n tags: operation.tags,\n description: operation.description,\n requestBody: Object.keys(bodySchema).length\n ? {\n required: required.length ? true : false,\n content: {\n [operation.contentType || 'application/json']: {\n schema: {\n required: required.length ? required : undefined,\n type: 'object',\n properties: bodySchema,\n },\n },\n },\n }\n : undefined,\n responses:\n Object.keys(operation.responses).length === 0\n ? undefined\n : operation.responses,\n };\n if (!operations[path]) {\n operations[path] = {};\n }\n operations[path][method] = operationObject;\n if (this.#onOperation) {\n const paths = this.#onOperation?.(\n operation.sourceFile,\n method,\n path,\n operationObject,\n );\n Object.assign(operations, paths ?? {});\n }\n }\n return operations;\n }\n\n /**\n * Converts Express/Node.js style path parameters (/path/:param) to OpenAPI style (/path/{param})\n */\n #tunePath(path: string): string {\n return path.replace(/:([^/]+)/g, '{$1}');\n }\n}\n\nasync function evalZod(schema: string, imports: InjectImport[] = []) {\n // https://github.com/nodejs/node/issues/51956\n const lines = [\n `import { createRequire } from \"node:module\";`,\n `const filename = \"${import.meta.url}\";`,\n `const require = createRequire(filename);`,\n `const z = require(\"zod\");`,\n ...imports.map((imp) => `const ${imp.import} = require('${imp.from}');`),\n `const {zodToJsonSchema} = require('zod-to-json-schema');`,\n `const schema = ${schema.replace('.optional()', '').replaceAll('instanceof(File)', 'string().base64()')};`,\n `const jsonSchema = zodToJsonSchema(schema, {\n $refStrategy: 'root',\n basePath: ['#', 'components', 'schemas'],\n target: 'jsonSchema7',\n base64Strategy: 'format:binary',\n });`,\n `export default jsonSchema;`,\n ];\n\n const base64 = Buffer.from(lines.join('\\n')).toString('base64');\n /* @vite-ignore */\n return import(`data:text/javascript;base64,${base64}`)\n .then((mod) => mod.default)\n .then(({ $schema, ...result }) => result);\n}\n\ninterface DateType {\n [$types]: any[];\n kind: string;\n optional: boolean;\n value?: string;\n}\n\nexport function toSchema(data: DateType | string | null | undefined): any {\n if (data === null || data === undefined) {\n return { type: 'any' };\n } else if (typeof data === 'string') {\n const isRef = data.startsWith('#');\n if (isRef) {\n return { $ref: data };\n }\n return { type: data };\n } else if (data.kind === 'literal') {\n return { enum: [data.value], type: data[$types][0] };\n } else if (data.kind === 'record') {\n return {\n type: 'object',\n additionalProperties: toSchema(data[$types][0]),\n };\n } else if (data.kind === 'array') {\n const items = data[$types].map(toSchema);\n return { type: 'array', items: data[$types].length ? items[0] : {} };\n } else if (data.kind === 'union') {\n return { anyOf: data[$types].map(toSchema) };\n } else if (data.kind === 'intersection') {\n return { allOf: data[$types].map(toSchema) };\n } else if ($types in data) {\n return data[$types].map(toSchema)[0] ?? {};\n } else {\n const props: Record<string, unknown> = {};\n const required: string[] = [];\n for (const [key, value] of Object.entries(data)) {\n props[key] = toSchema(value as any);\n if (!(value as any).optional) {\n required.push(key);\n }\n }\n return {\n type: 'object',\n properties: props,\n required,\n additionalProperties: false,\n };\n }\n}\n\nexport function isHttpMethod(name: string): name is Method {\n return ['get', 'post', 'put', 'delete', 'patch'].includes(name);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/lib/ref.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OpenAPIObject, ParameterObject, ReferenceObject, RequestBodyObject, SchemaObject } from 'openapi3-ts/oas31';
|
|
1
|
+
import type { HeaderObject, OpenAPIObject, ParameterObject, ReferenceObject, RequestBodyObject, SchemaObject, SecuritySchemeObject } from 'openapi3-ts/oas31';
|
|
2
2
|
export declare function isRef(obj: any): obj is ReferenceObject;
|
|
3
3
|
export declare function notRef(obj: any): obj is SchemaObject;
|
|
4
4
|
export declare function cleanRef(ref: string): string;
|
|
@@ -7,5 +7,8 @@ export declare function parseRef(ref: string): {
|
|
|
7
7
|
namespace: string;
|
|
8
8
|
path: string;
|
|
9
9
|
};
|
|
10
|
-
export declare function
|
|
10
|
+
export declare function resolveRef<T extends SchemaObject | HeaderObject | ParameterObject | ReferenceObject | RequestBodyObject = SchemaObject>(spec: OpenAPIObject, maybeRef: SchemaObject | ReferenceObject | ParameterObject | SecuritySchemeObject): T;
|
|
11
|
+
export declare function followRef<T extends SchemaObject | HeaderObject | ParameterObject | ReferenceObject | RequestBodyObject = SchemaObject>(spec: OpenAPIObject, ref: string): T;
|
|
12
|
+
export declare function tapRef<T extends SchemaObject | HeaderObject | ParameterObject | ReferenceObject | RequestBodyObject = SchemaObject>(spec: OpenAPIObject, maybeRef: SchemaObject | ReferenceObject): T;
|
|
13
|
+
export declare function distillRef<T extends SchemaObject | HeaderObject | ParameterObject | ReferenceObject | RequestBodyObject = SchemaObject>(spec: OpenAPIObject, ref: string): T;
|
|
11
14
|
//# sourceMappingURL=ref.d.ts.map
|
package/dist/lib/ref.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ref.d.ts","sourceRoot":"","sources":["../../src/lib/ref.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,YAAY,
|
|
1
|
+
{"version":3,"file":"ref.d.ts","sourceRoot":"","sources":["../../src/lib/ref.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAI3B,wBAAgB,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,eAAe,CAEtD;AACD,wBAAgB,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,YAAY,CAEpD;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,UAEnC;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM;;;;EAgBnC;AAED,wBAAgB,UAAU,CACxB,CAAC,SACG,YAAY,GACZ,YAAY,GACZ,eAAe,GACf,eAAe,GACf,iBAAiB,GAAG,YAAY,EAEpC,IAAI,EAAE,aAAa,EACnB,QAAQ,EACJ,YAAY,GACZ,eAAe,GACf,eAAe,GACf,oBAAoB,GACvB,CAAC,CAKH;AAED,wBAAgB,SAAS,CACvB,CAAC,SACG,YAAY,GACZ,YAAY,GACZ,eAAe,GACf,eAAe,GACf,iBAAiB,GAAG,YAAY,EACpC,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,CAOrC;AAED,wBAAgB,MAAM,CACpB,CAAC,SACG,YAAY,GACZ,YAAY,GACZ,eAAe,GACf,eAAe,GACf,iBAAiB,GAAG,YAAY,EACpC,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,GAAG,eAAe,GAAG,CAAC,CAMlE;AAED,wBAAgB,UAAU,CACxB,CAAC,SACG,YAAY,GACZ,YAAY,GACZ,eAAe,GACf,eAAe,GACf,iBAAiB,GAAG,YAAY,EACpC,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,CA8CrC"}
|
package/dist/lib/ref.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { get } from "lodash-es";
|
|
2
|
+
import { isEmpty } from "./utils.js";
|
|
2
3
|
function isRef(obj) {
|
|
3
4
|
return obj && "$ref" in obj;
|
|
4
5
|
}
|
|
@@ -10,27 +11,95 @@ function cleanRef(ref) {
|
|
|
10
11
|
}
|
|
11
12
|
function parseRef(ref) {
|
|
12
13
|
const parts = ref.split("/");
|
|
14
|
+
const names = [];
|
|
13
15
|
const [model] = parts.splice(-1);
|
|
16
|
+
names.push(model);
|
|
17
|
+
while (parts.lastIndexOf("properties") !== -1) {
|
|
18
|
+
parts.splice(parts.lastIndexOf("properties"), 1);
|
|
19
|
+
const [model2] = parts.splice(-1);
|
|
20
|
+
names.push(model2);
|
|
21
|
+
}
|
|
14
22
|
const [namespace] = parts.splice(-1);
|
|
15
23
|
return {
|
|
16
|
-
model,
|
|
24
|
+
model: names.reverse().join(" "),
|
|
17
25
|
namespace,
|
|
18
26
|
path: cleanRef(parts.join("/"))
|
|
19
27
|
};
|
|
20
28
|
}
|
|
29
|
+
function resolveRef(spec, maybeRef) {
|
|
30
|
+
if (isRef(maybeRef)) {
|
|
31
|
+
return followRef(spec, maybeRef.$ref);
|
|
32
|
+
}
|
|
33
|
+
return maybeRef;
|
|
34
|
+
}
|
|
21
35
|
function followRef(spec, ref) {
|
|
22
36
|
const pathParts = cleanRef(ref).split("/");
|
|
23
37
|
const entry = get(spec, pathParts);
|
|
24
|
-
if (entry
|
|
38
|
+
if (isRef(entry)) {
|
|
25
39
|
return followRef(spec, entry.$ref);
|
|
26
40
|
}
|
|
27
41
|
return entry;
|
|
28
42
|
}
|
|
43
|
+
function tapRef(spec, maybeRef) {
|
|
44
|
+
if (isRef(maybeRef)) {
|
|
45
|
+
const pathParts = cleanRef(maybeRef.$ref).split("/");
|
|
46
|
+
return get(spec, pathParts);
|
|
47
|
+
}
|
|
48
|
+
return maybeRef;
|
|
49
|
+
}
|
|
50
|
+
function distillRef(spec, ref) {
|
|
51
|
+
const def = followRef(spec, ref);
|
|
52
|
+
if (!def) {
|
|
53
|
+
return def;
|
|
54
|
+
}
|
|
55
|
+
if ("properties" in def) {
|
|
56
|
+
def.properties ??= {};
|
|
57
|
+
for (const key in def.properties) {
|
|
58
|
+
const prop = def.properties[key];
|
|
59
|
+
if (isRef(prop)) {
|
|
60
|
+
def.properties[key] = distillRef(spec, prop.$ref);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if ("items" in def) {
|
|
65
|
+
if (isRef(def.items)) {
|
|
66
|
+
def.items = distillRef(spec, def.items.$ref);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if ("allOf" in def && !isEmpty(def.allOf)) {
|
|
70
|
+
def.allOf = def.allOf.map((item) => {
|
|
71
|
+
if (isRef(item)) {
|
|
72
|
+
return distillRef(spec, item.$ref);
|
|
73
|
+
}
|
|
74
|
+
return item;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if ("oneOf" in def && !isEmpty(def.oneOf)) {
|
|
78
|
+
def.oneOf = def.oneOf.map((item) => {
|
|
79
|
+
if (isRef(item)) {
|
|
80
|
+
return distillRef(spec, item.$ref);
|
|
81
|
+
}
|
|
82
|
+
return item;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if ("anyOf" in def && !isEmpty(def.anyOf)) {
|
|
86
|
+
def.anyOf = def.anyOf.map((item) => {
|
|
87
|
+
if (isRef(item)) {
|
|
88
|
+
return distillRef(spec, item.$ref);
|
|
89
|
+
}
|
|
90
|
+
return item;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return def;
|
|
94
|
+
}
|
|
29
95
|
export {
|
|
30
96
|
cleanRef,
|
|
97
|
+
distillRef,
|
|
31
98
|
followRef,
|
|
32
99
|
isRef,
|
|
33
100
|
notRef,
|
|
34
|
-
parseRef
|
|
101
|
+
parseRef,
|
|
102
|
+
resolveRef,
|
|
103
|
+
tapRef
|
|
35
104
|
};
|
|
36
105
|
//# sourceMappingURL=ref.js.map
|
package/dist/lib/ref.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/ref.ts"],
|
|
4
|
-
"sourcesContent": ["import { get } from 'lodash-es';\nimport type {\n OpenAPIObject,\n ParameterObject,\n ReferenceObject,\n RequestBodyObject,\n SchemaObject,\n} from 'openapi3-ts/oas31';\n\nexport function isRef(obj: any): obj is ReferenceObject {\n return obj && '$ref' in obj;\n}\nexport function notRef(obj: any): obj is SchemaObject {\n return !isRef(obj);\n}\n\nexport function cleanRef(ref: string) {\n return ref.replace(/^#\\//, '');\n}\n\nexport function parseRef(ref: string) {\n const parts = ref.split('/');\n const [model] = parts.splice(-1);\n const [namespace] = parts.splice(-1);\n return {\n model,\n namespace,\n path: cleanRef(parts.join('/')),\n };\n}\nexport function followRef<\n T extends SchemaObject | ParameterObject | RequestBodyObject = SchemaObject,\n>(spec: OpenAPIObject, ref: string): T {\n const pathParts = cleanRef(ref).split('/');\n const entry = get(spec, pathParts) as T | ReferenceObject;\n if (entry && '
|
|
5
|
-
"mappings": "AAAA,SAAS,WAAW;
|
|
6
|
-
"names": []
|
|
4
|
+
"sourcesContent": ["import { get } from 'lodash-es';\nimport type {\n HeaderObject,\n OpenAPIObject,\n ParameterObject,\n ReferenceObject,\n RequestBodyObject,\n SchemaObject,\n SecuritySchemeObject,\n} from 'openapi3-ts/oas31';\n\nimport { isEmpty } from './utils.js';\n\nexport function isRef(obj: any): obj is ReferenceObject {\n return obj && '$ref' in obj;\n}\nexport function notRef(obj: any): obj is SchemaObject {\n return !isRef(obj);\n}\n\nexport function cleanRef(ref: string) {\n return ref.replace(/^#\\//, '');\n}\n\nexport function parseRef(ref: string) {\n const parts = ref.split('/');\n const names: string[] = [];\n const [model] = parts.splice(-1);\n names.push(model);\n while (parts.lastIndexOf('properties') !== -1) {\n parts.splice(parts.lastIndexOf('properties'), 1);\n const [model] = parts.splice(-1);\n names.push(model);\n }\n const [namespace] = parts.splice(-1);\n return {\n model: names.reverse().join(' '),\n namespace,\n path: cleanRef(parts.join('/')),\n };\n}\n\nexport function resolveRef<\n T extends\n | SchemaObject\n | HeaderObject\n | ParameterObject\n | ReferenceObject\n | RequestBodyObject = SchemaObject,\n>(\n spec: OpenAPIObject,\n maybeRef:\n | SchemaObject\n | ReferenceObject\n | ParameterObject\n | SecuritySchemeObject,\n): T {\n if (isRef(maybeRef)) {\n return followRef<T>(spec, maybeRef.$ref!);\n }\n return maybeRef as T;\n}\n\nexport function followRef<\n T extends\n | SchemaObject\n | HeaderObject\n | ParameterObject\n | ReferenceObject\n | RequestBodyObject = SchemaObject,\n>(spec: OpenAPIObject, ref: string): T {\n const pathParts = cleanRef(ref).split('/');\n const entry = get(spec, pathParts) as T | ReferenceObject;\n if (isRef(entry)) {\n return followRef<T>(spec, entry.$ref!);\n }\n return entry;\n}\n\nexport function tapRef<\n T extends\n | SchemaObject\n | HeaderObject\n | ParameterObject\n | ReferenceObject\n | RequestBodyObject = SchemaObject,\n>(spec: OpenAPIObject, maybeRef: SchemaObject | ReferenceObject): T {\n if (isRef(maybeRef)) {\n const pathParts = cleanRef(maybeRef.$ref).split('/');\n return get(spec, pathParts) as T;\n }\n return maybeRef as T;\n}\n\nexport function distillRef<\n T extends\n | SchemaObject\n | HeaderObject\n | ParameterObject\n | ReferenceObject\n | RequestBodyObject = SchemaObject,\n>(spec: OpenAPIObject, ref: string): T {\n const def = followRef<T>(spec, ref);\n if (!def) {\n return def;\n }\n\n if ('properties' in def) {\n def.properties ??= {};\n for (const key in def.properties) {\n const prop = def.properties[key];\n if (isRef(prop)) {\n def.properties[key] = distillRef(spec, prop.$ref);\n }\n }\n }\n if ('items' in def) {\n if (isRef(def.items)) {\n def.items = distillRef<SchemaObject>(spec, def.items.$ref);\n }\n }\n if ('allOf' in def && !isEmpty(def.allOf)) {\n def.allOf = def.allOf.map((item) => {\n if (isRef(item)) {\n return distillRef<SchemaObject>(spec, item.$ref);\n }\n return item;\n });\n }\n if ('oneOf' in def && !isEmpty(def.oneOf)) {\n def.oneOf = def.oneOf.map((item) => {\n if (isRef(item)) {\n return distillRef<SchemaObject>(spec, item.$ref);\n }\n return item;\n });\n }\n if ('anyOf' in def && !isEmpty(def.anyOf)) {\n def.anyOf = def.anyOf.map((item) => {\n if (isRef(item)) {\n return distillRef<SchemaObject>(spec, item.$ref);\n }\n return item;\n });\n }\n\n return def;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["model"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type ts from 'typescript';
|
|
2
|
+
import type { TypeDeriver } from './deriver';
|
|
3
|
+
import type { ResponseItem } from './paths';
|
|
4
|
+
export declare function removeDuplicates<T>(data: T[], accessor?: (item: T) => T[keyof T] | T): T[];
|
|
5
|
+
export type InferRecordValue<T> = T extends Record<string, infer U> ? U : any;
|
|
6
|
+
export declare function toLitObject<T extends Record<string, any>>(obj: T, accessor?: (value: InferRecordValue<T>) => string): string;
|
|
7
|
+
export type NaunceResponseAnalyzerFn = (handler: ts.ArrowFunction, deriver: TypeDeriver, node: ts.Node) => ResponseItem[];
|
|
8
|
+
export type NaunceResponseAnalyzer = Record<string, NaunceResponseAnalyzerFn>;
|
|
9
|
+
export type ResponseAnalyzerFn = (handler: ts.ArrowFunction, deriver: TypeDeriver) => ResponseItem[];
|
|
10
|
+
export declare function isEmpty(value: unknown): value is null | undefined | '';
|
|
11
|
+
export declare function pascalcase(value: string): string;
|
|
12
|
+
export declare function spinalcase(value: string): string;
|
|
13
|
+
export declare function snakecase(value: string): string;
|
|
14
|
+
export declare function camelcase(value: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Joins an array of strings so that elements containing only digits
|
|
17
|
+
* are concatenated without a separator, while all other elements
|
|
18
|
+
* are prefixed by the separator (unless they're the very first element).
|
|
19
|
+
*/
|
|
20
|
+
export declare function joinSkipDigits(arr: string[], separator: string): string;
|
|
21
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,IAAI,EAAE,CAAC,EAAE,EACT,QAAQ,GAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAkB,GACrD,CAAC,EAAE,CAEL;AAED,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;AAE9E,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACvD,GAAG,EAAE,CAAC,EACN,QAAQ,GAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,MAAyB,UAKpE;AAED,MAAM,MAAM,wBAAwB,GAAG,CACrC,OAAO,EAAE,EAAE,CAAC,aAAa,EACzB,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,EAAE,CAAC,IAAI,KACV,YAAY,EAAE,CAAC;AACpB,MAAM,MAAM,sBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;AAE9E,MAAM,MAAM,kBAAkB,GAAG,CAC/B,OAAO,EAAE,EAAE,CAAC,aAAa,EACzB,OAAO,EAAE,WAAW,KACjB,YAAY,EAAE,CAAC;AAEpB,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,GAAG,SAAS,GAAG,EAAE,CAWtE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,UAEvC;AACD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,UAEvC;AACD,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,UAEtC;AACD,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAgBvE"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {
|
|
2
|
+
camelcase as _camelcase,
|
|
3
|
+
pascalcase as _pascalcase,
|
|
4
|
+
snakecase as _snakecase,
|
|
5
|
+
spinalcase as _spinalcase
|
|
6
|
+
} from "stringcase";
|
|
7
|
+
function removeDuplicates(data, accessor = (item) => item) {
|
|
8
|
+
return [...new Map(data.map((x) => [accessor(x), x])).values()];
|
|
9
|
+
}
|
|
10
|
+
function toLitObject(obj, accessor = (value) => value) {
|
|
11
|
+
return `{${Object.keys(obj).map((key) => `${key}: ${accessor(obj[key])}`).join(", ")}}`;
|
|
12
|
+
}
|
|
13
|
+
function isEmpty(value) {
|
|
14
|
+
if (value === null || value === void 0 || value === "") {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
if (typeof value === "object" && Object.keys(value).length === 0) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
function pascalcase(value) {
|
|
26
|
+
return _pascalcase(value.split("/").join(" "));
|
|
27
|
+
}
|
|
28
|
+
function spinalcase(value) {
|
|
29
|
+
return _spinalcase(value.split("/").join(" "));
|
|
30
|
+
}
|
|
31
|
+
function snakecase(value) {
|
|
32
|
+
return _snakecase(value.split("/").join(" "));
|
|
33
|
+
}
|
|
34
|
+
function camelcase(value) {
|
|
35
|
+
const cleaned = value.replace(/[^A-Za-z0-9]+(?=\d)/g, "");
|
|
36
|
+
return _camelcase(cleaned);
|
|
37
|
+
}
|
|
38
|
+
function joinSkipDigits(arr, separator) {
|
|
39
|
+
if (arr.length === 0)
|
|
40
|
+
return "";
|
|
41
|
+
let result = arr[0];
|
|
42
|
+
for (let i = 1; i < arr.length; i++) {
|
|
43
|
+
const el = arr[i];
|
|
44
|
+
if (/^\d+$/.test(el)) {
|
|
45
|
+
result += el;
|
|
46
|
+
} else {
|
|
47
|
+
result += separator + el;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
camelcase,
|
|
54
|
+
isEmpty,
|
|
55
|
+
joinSkipDigits,
|
|
56
|
+
pascalcase,
|
|
57
|
+
removeDuplicates,
|
|
58
|
+
snakecase,
|
|
59
|
+
spinalcase,
|
|
60
|
+
toLitObject
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/lib/utils.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n camelcase as _camelcase,\n pascalcase as _pascalcase,\n snakecase as _snakecase,\n spinalcase as _spinalcase,\n} from 'stringcase';\nimport type ts from 'typescript';\n\nimport type { TypeDeriver } from './deriver';\nimport type { ResponseItem } from './paths';\n\nexport function removeDuplicates<T>(\n data: T[],\n accessor: (item: T) => T[keyof T] | T = (item) => item,\n): T[] {\n return [...new Map(data.map((x) => [accessor(x), x])).values()];\n}\n\nexport type InferRecordValue<T> = T extends Record<string, infer U> ? U : any;\n\nexport function toLitObject<T extends Record<string, any>>(\n obj: T,\n accessor: (value: InferRecordValue<T>) => string = (value) => value,\n) {\n return `{${Object.keys(obj)\n .map((key) => `${key}: ${accessor(obj[key])}`)\n .join(', ')}}`;\n}\n\nexport type NaunceResponseAnalyzerFn = (\n handler: ts.ArrowFunction,\n deriver: TypeDeriver,\n node: ts.Node,\n) => ResponseItem[];\nexport type NaunceResponseAnalyzer = Record<string, NaunceResponseAnalyzerFn>;\n\nexport type ResponseAnalyzerFn = (\n handler: ts.ArrowFunction,\n deriver: TypeDeriver,\n) => ResponseItem[];\n\nexport function isEmpty(value: unknown): value is null | undefined | '' {\n if (value === null || value === undefined || value === '') {\n return true;\n }\n if (Array.isArray(value) && value.length === 0) {\n return true;\n }\n if (typeof value === 'object' && Object.keys(value).length === 0) {\n return true;\n }\n return false;\n}\n\nexport function pascalcase(value: string) {\n return _pascalcase(value.split('/').join(' '));\n}\nexport function spinalcase(value: string) {\n return _spinalcase(value.split('/').join(' '));\n}\nexport function snakecase(value: string) {\n return _snakecase(value.split('/').join(' '));\n}\nexport function camelcase(value: string): string {\n // remove any \u201Cspecial\u201D chars immediately preceding a digit\n const cleaned = value.replace(/[^A-Za-z0-9]+(?=\\d)/g, '');\n return _camelcase(cleaned);\n}\n\n/**\n * Joins an array of strings so that elements containing only digits\n * are concatenated without a separator, while all other elements\n * are prefixed by the separator (unless they're the very first element).\n */\nexport function joinSkipDigits(arr: string[], separator: string): string {\n if (arr.length === 0) return '';\n\n let result = arr[0];\n for (let i = 1; i < arr.length; i++) {\n const el = arr[i];\n // If this element is digits-only, append it directly\n if (/^\\d+$/.test(el)) {\n result += el;\n } else {\n // Otherwise, prepend the separator and append the element\n result += separator + el;\n }\n }\n\n return result;\n}\n"],
|
|
5
|
+
"mappings": "AAAA;AAAA,EACE,aAAa;AAAA,EACb,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAAA,OACT;AAMA,SAAS,iBACd,MACA,WAAwC,CAAC,SAAS,MAC7C;AACL,SAAO,CAAC,GAAG,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;AAChE;AAIO,SAAS,YACd,KACA,WAAmD,CAAC,UAAU,OAC9D;AACA,SAAO,IAAI,OAAO,KAAK,GAAG,EACvB,IAAI,CAAC,QAAQ,GAAG,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,CAAC,EAAE,EAC5C,KAAK,IAAI,CAAC;AACf;AAcO,SAAS,QAAQ,OAAgD;AACtE,MAAI,UAAU,QAAQ,UAAU,UAAa,UAAU,IAAI;AACzD,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;AAC9C,WAAO;AAAA,EACT;AACA,MAAI,OAAO,UAAU,YAAY,OAAO,KAAK,KAAK,EAAE,WAAW,GAAG;AAChE,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,SAAS,WAAW,OAAe;AACxC,SAAO,YAAY,MAAM,MAAM,GAAG,EAAE,KAAK,GAAG,CAAC;AAC/C;AACO,SAAS,WAAW,OAAe;AACxC,SAAO,YAAY,MAAM,MAAM,GAAG,EAAE,KAAK,GAAG,CAAC;AAC/C;AACO,SAAS,UAAU,OAAe;AACvC,SAAO,WAAW,MAAM,MAAM,GAAG,EAAE,KAAK,GAAG,CAAC;AAC9C;AACO,SAAS,UAAU,OAAuB;AAE/C,QAAM,UAAU,MAAM,QAAQ,wBAAwB,EAAE;AACxD,SAAO,WAAW,OAAO;AAC3B;AAOO,SAAS,eAAe,KAAe,WAA2B;AACvE,MAAI,IAAI,WAAW;AAAG,WAAO;AAE7B,MAAI,SAAS,IAAI,CAAC;AAClB,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAM,KAAK,IAAI,CAAC;AAEhB,QAAI,QAAQ,KAAK,EAAE,GAAG;AACpB,gBAAU;AAAA,IACZ,OAAO;AAEL,gBAAU,YAAY;AAAA,IACxB;AAAA,EACF;AAEA,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|