kubernetes-fluent-client 3.0.3 → 4.0.0-rc-http2-watch
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/.prettierignore +4 -0
- package/README.md +24 -0
- package/dist/cli.js +21 -1
- package/dist/fileSystem.d.ts +11 -0
- package/dist/fileSystem.d.ts.map +1 -0
- package/dist/fileSystem.js +42 -0
- package/dist/fileSystem.test.d.ts +2 -0
- package/dist/fileSystem.test.d.ts.map +1 -0
- package/dist/fileSystem.test.js +75 -0
- package/dist/fluent/watch.d.ts +2 -0
- package/dist/fluent/watch.d.ts.map +1 -1
- package/dist/fluent/watch.js +147 -27
- package/dist/generate.d.ts +71 -11
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +130 -117
- package/dist/generate.test.js +293 -346
- package/dist/postProcessing.d.ts +246 -0
- package/dist/postProcessing.d.ts.map +1 -0
- package/dist/postProcessing.js +497 -0
- package/dist/postProcessing.test.d.ts +2 -0
- package/dist/postProcessing.test.d.ts.map +1 -0
- package/dist/postProcessing.test.js +550 -0
- package/e2e/cli.e2e.test.ts +127 -0
- package/e2e/crds/policyreports.default.expected/policyreport-v1alpha1.ts +332 -0
- package/e2e/crds/policyreports.default.expected/policyreport-v1alpha2.ts +360 -0
- package/e2e/crds/policyreports.default.expected/policyreport-v1beta1.ts +360 -0
- package/e2e/crds/policyreports.no.post.expected/policyreport-v1alpha1.ts +331 -0
- package/e2e/crds/policyreports.no.post.expected/policyreport-v1alpha2.ts +360 -0
- package/e2e/crds/policyreports.no.post.expected/policyreport-v1beta1.ts +360 -0
- package/e2e/crds/test.yaml/policyreports.test.yaml +1008 -0
- package/e2e/crds/test.yaml/uds-podmonitors.test.yaml +1245 -0
- package/e2e/crds/uds-podmonitors.default.expected/podmonitor-v1.ts +1333 -0
- package/e2e/crds/uds-podmonitors.no.post.expected/podmonitor-v1.ts +1360 -0
- package/package.json +6 -5
- package/src/cli.ts +25 -1
- package/src/fileSystem.test.ts +67 -0
- package/src/fileSystem.ts +25 -0
- package/src/fluent/watch.ts +174 -35
- package/src/generate.test.ts +368 -358
- package/src/generate.ts +173 -154
- package/src/postProcessing.test.ts +742 -0
- package/src/postProcessing.ts +568 -0
package/src/generate.ts
CHANGED
|
@@ -18,197 +18,219 @@ import { CustomResourceDefinition } from "./upstream";
|
|
|
18
18
|
import { LogFn } from "./types";
|
|
19
19
|
|
|
20
20
|
export interface GenerateOptions {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
directory?: string;
|
|
25
|
-
/** Disable kubernetes-fluent-client wrapping */
|
|
26
|
-
plain?: boolean;
|
|
27
|
-
/** The language to generate types in */
|
|
21
|
+
source: string; // URL, file path, or K8s CRD name
|
|
22
|
+
directory?: string; // Output directory path
|
|
23
|
+
plain?: boolean; // Disable fluent client wrapping
|
|
28
24
|
language?: string | TargetLanguage;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
logFn: LogFn;
|
|
25
|
+
npmPackage?: string; // Override NPM package
|
|
26
|
+
logFn: LogFn; // Log function callback
|
|
27
|
+
noPost?: boolean; // Enable/disable post-processing
|
|
33
28
|
}
|
|
34
29
|
|
|
35
30
|
/**
|
|
36
31
|
* Converts a CustomResourceDefinition to TypeScript types
|
|
37
32
|
*
|
|
38
|
-
* @param crd The CustomResourceDefinition to convert
|
|
39
|
-
* @param opts The options
|
|
40
|
-
* @returns A promise that resolves
|
|
33
|
+
* @param crd - The CustomResourceDefinition object to convert.
|
|
34
|
+
* @param opts - The options for generating the TypeScript types.
|
|
35
|
+
* @returns A promise that resolves to a record of generated TypeScript types.
|
|
41
36
|
*/
|
|
42
|
-
async function convertCRDtoTS(
|
|
37
|
+
export async function convertCRDtoTS(
|
|
43
38
|
crd: CustomResourceDefinition,
|
|
44
39
|
opts: GenerateOptions,
|
|
45
|
-
): Promise<
|
|
46
|
-
|
|
40
|
+
): Promise<
|
|
41
|
+
{
|
|
42
|
+
results: Record<string, string[]>;
|
|
43
|
+
name: string;
|
|
44
|
+
crd: CustomResourceDefinition;
|
|
45
|
+
version: string;
|
|
46
|
+
}[]
|
|
47
|
+
> {
|
|
47
48
|
const name = crd.spec.names.kind;
|
|
48
|
-
|
|
49
49
|
const results: Record<string, string[]> = {};
|
|
50
|
+
const output: {
|
|
51
|
+
results: Record<string, string[]>;
|
|
52
|
+
name: string;
|
|
53
|
+
crd: CustomResourceDefinition;
|
|
54
|
+
version: string;
|
|
55
|
+
}[] = [];
|
|
56
|
+
|
|
57
|
+
// Check for missing versions or empty schema
|
|
58
|
+
if (!crd.spec.versions || crd.spec.versions.length === 0) {
|
|
59
|
+
opts.logFn(`Skipping ${crd.metadata?.name}, it does not appear to be a CRD`);
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
50
62
|
|
|
63
|
+
// Iterate through each version of the CRD
|
|
51
64
|
for (const match of crd.spec.versions) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// Create a new JSONSchemaInput
|
|
58
|
-
const schemaInput = new JSONSchemaInput(new FetchingJSONSchemaStore());
|
|
59
|
-
|
|
60
|
-
opts.logFn(`- Generating ${crd.spec.group}/${version} types for ${name}`);
|
|
61
|
-
|
|
62
|
-
// Add the schema to the input
|
|
63
|
-
await schemaInput.addSource({ name, schema });
|
|
64
|
-
|
|
65
|
-
// Create a new InputData object
|
|
66
|
-
const inputData = new InputData();
|
|
67
|
-
inputData.addInput(schemaInput);
|
|
68
|
-
|
|
69
|
-
// If the language is not specified, default to TypeScript
|
|
70
|
-
if (!opts.language) {
|
|
71
|
-
opts.language = "ts";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Generate the types
|
|
75
|
-
const out = await quicktype({
|
|
76
|
-
inputData,
|
|
77
|
-
lang: opts.language,
|
|
78
|
-
rendererOptions: { "just-types": "true" },
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
let processedLines = out.lines;
|
|
82
|
-
|
|
83
|
-
// If using typescript, remove the line containing `[property: string]: any;`
|
|
84
|
-
if (opts.language === "ts" || opts.language === "typescript") {
|
|
85
|
-
processedLines = out.lines.filter(line => !line.includes("[property: string]: any;"));
|
|
65
|
+
if (!match.schema?.openAPIV3Schema) {
|
|
66
|
+
opts.logFn(
|
|
67
|
+
`Skipping ${crd.metadata?.name ?? "unknown"}, it does not appear to have a valid schema`,
|
|
68
|
+
);
|
|
69
|
+
continue;
|
|
86
70
|
}
|
|
87
71
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (!opts.npmPackage) {
|
|
91
|
-
opts.npmPackage = "kubernetes-fluent-client";
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
processedLines.unshift(
|
|
95
|
-
// Add warning that the file is auto-generated
|
|
96
|
-
`// This file is auto-generated by ${opts.npmPackage}, do not edit manually\n`,
|
|
97
|
-
// Add the imports before any other lines
|
|
98
|
-
`import { GenericKind, RegisterKind } from "${opts.npmPackage}";\n`,
|
|
99
|
-
);
|
|
72
|
+
const schema = JSON.stringify(match.schema.openAPIV3Schema);
|
|
73
|
+
opts.logFn(`- Generating ${crd.spec.group}/${match.name} types for ${name}`);
|
|
100
74
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
line.includes(`export interface ${name} {`),
|
|
104
|
-
);
|
|
75
|
+
const inputData = await prepareInputData(name, schema);
|
|
76
|
+
const generatedTypes = await generateTypes(inputData, opts);
|
|
105
77
|
|
|
106
|
-
|
|
107
|
-
|
|
78
|
+
const fileName = `${name.toLowerCase()}-${match.name.toLowerCase()}`;
|
|
79
|
+
writeGeneratedFile(fileName, opts.directory || "", generatedTypes, opts.language || "ts");
|
|
108
80
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
processedLines.push(` version: "${version}",`);
|
|
113
|
-
processedLines.push(` kind: "${name}",`);
|
|
114
|
-
processedLines.push(` plural: "${crd.spec.names.plural}",`);
|
|
115
|
-
processedLines.push(`});`);
|
|
116
|
-
}
|
|
81
|
+
results[fileName] = generatedTypes;
|
|
82
|
+
output.push({ results, name, crd, version: match.name });
|
|
83
|
+
}
|
|
117
84
|
|
|
118
|
-
|
|
119
|
-
|
|
85
|
+
return output;
|
|
86
|
+
}
|
|
120
87
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Prepares the input data for quicktype from the provided schema.
|
|
90
|
+
*
|
|
91
|
+
* @param name - The name of the schema.
|
|
92
|
+
* @param schema - The JSON schema as a string.
|
|
93
|
+
* @returns A promise that resolves to the input data for quicktype.
|
|
94
|
+
*/
|
|
95
|
+
export async function prepareInputData(name: string, schema: string): Promise<InputData> {
|
|
96
|
+
// Create a new JSONSchemaInput
|
|
97
|
+
const schemaInput = new JSONSchemaInput(new FetchingJSONSchemaStore());
|
|
125
98
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
fs.writeFileSync(filePath, finalContents);
|
|
129
|
-
}
|
|
99
|
+
// Add the schema to the input
|
|
100
|
+
await schemaInput.addSource({ name, schema });
|
|
130
101
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
102
|
+
// Create a new InputData object
|
|
103
|
+
const inputData = new InputData();
|
|
104
|
+
inputData.addInput(schemaInput);
|
|
134
105
|
|
|
135
|
-
return
|
|
106
|
+
return inputData;
|
|
136
107
|
}
|
|
137
108
|
|
|
138
109
|
/**
|
|
139
|
-
*
|
|
110
|
+
* Generates TypeScript types using quicktype.
|
|
140
111
|
*
|
|
141
|
-
* @param
|
|
142
|
-
* @
|
|
112
|
+
* @param inputData - The input data for quicktype.
|
|
113
|
+
* @param opts - The options for generating the TypeScript types.
|
|
114
|
+
* @returns A promise that resolves to an array of generated TypeScript type lines.
|
|
143
115
|
*/
|
|
144
|
-
async function
|
|
145
|
-
|
|
146
|
-
|
|
116
|
+
export async function generateTypes(
|
|
117
|
+
inputData: InputData,
|
|
118
|
+
opts: GenerateOptions,
|
|
119
|
+
): Promise<string[]> {
|
|
120
|
+
// If the language is not specified, default to TypeScript
|
|
121
|
+
const language = opts.language || "ts";
|
|
122
|
+
|
|
123
|
+
// Generate the types
|
|
124
|
+
const out = await quicktype({
|
|
125
|
+
inputData,
|
|
126
|
+
lang: language,
|
|
127
|
+
rendererOptions: { "just-types": "true" },
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return out.lines;
|
|
131
|
+
}
|
|
147
132
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Writes the processed lines to the output file.
|
|
135
|
+
*
|
|
136
|
+
* @param fileName - The name of the file to write.
|
|
137
|
+
* @param directory - The directory where the file will be written.
|
|
138
|
+
* @param content - The content to write to the file.
|
|
139
|
+
* @param language - The programming language of the file.
|
|
140
|
+
*/
|
|
141
|
+
export function writeGeneratedFile(
|
|
142
|
+
fileName: string,
|
|
143
|
+
directory: string,
|
|
144
|
+
content: string[],
|
|
145
|
+
language: string | TargetLanguage,
|
|
146
|
+
): void {
|
|
147
|
+
language = language || "ts";
|
|
148
|
+
if (!directory) return;
|
|
149
|
+
|
|
150
|
+
const filePath = path.join(directory, `${fileName}.${language}`);
|
|
151
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
152
|
+
fs.writeFileSync(filePath, content.join("\n"));
|
|
153
|
+
}
|
|
155
154
|
|
|
156
|
-
|
|
155
|
+
/**
|
|
156
|
+
* Reads or fetches a CustomResourceDefinition from a file, URL, or the cluster.
|
|
157
|
+
*
|
|
158
|
+
* @param opts - The options for generating the TypeScript types.
|
|
159
|
+
* @returns A promise that resolves to an array of CustomResourceDefinition objects.
|
|
160
|
+
*/
|
|
161
|
+
export async function readOrFetchCrd(opts: GenerateOptions): Promise<CustomResourceDefinition[]> {
|
|
157
162
|
try {
|
|
163
|
+
const filePath = resolveFilePath(opts.source);
|
|
164
|
+
|
|
158
165
|
if (fs.existsSync(filePath)) {
|
|
159
|
-
logFn(`Attempting to load ${source} as a local file`);
|
|
160
|
-
const
|
|
161
|
-
return loadAllYaml(
|
|
166
|
+
opts.logFn(`Attempting to load ${opts.source} as a local file`);
|
|
167
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
168
|
+
return loadAllYaml(content) as CustomResourceDefinition[];
|
|
162
169
|
}
|
|
163
|
-
} catch {
|
|
164
|
-
// Ignore errors
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Next try to parse the source as a URL
|
|
168
|
-
try {
|
|
169
|
-
const url = new URL(source);
|
|
170
170
|
|
|
171
|
-
|
|
172
|
-
if (url
|
|
173
|
-
logFn(`Attempting to load ${source} as a URL`);
|
|
174
|
-
const { ok, data } = await fetch<string>(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (!ok) {
|
|
178
|
-
throw new Error(`Failed to fetch ${source}: ${data}`);
|
|
171
|
+
const url = tryParseUrl(opts.source);
|
|
172
|
+
if (url) {
|
|
173
|
+
opts.logFn(`Attempting to load ${opts.source} as a URL`);
|
|
174
|
+
const { ok, data } = await fetch<string>(url.href);
|
|
175
|
+
if (ok) {
|
|
176
|
+
return loadAllYaml(data) as CustomResourceDefinition[];
|
|
179
177
|
}
|
|
180
|
-
|
|
181
|
-
return loadAllYaml(data) as CustomResourceDefinition[];
|
|
182
|
-
}
|
|
183
|
-
} catch (e) {
|
|
184
|
-
// If invalid, ignore the error
|
|
185
|
-
if (e.code !== "ERR_INVALID_URL") {
|
|
186
|
-
throw new Error(`Error parsing URL ${source}`);
|
|
187
178
|
}
|
|
179
|
+
|
|
180
|
+
// Fallback to Kubernetes cluster
|
|
181
|
+
opts.logFn(`Attempting to read ${opts.source} from the Kubernetes cluster`);
|
|
182
|
+
return [await K8s(CustomResourceDefinition).Get(opts.source)];
|
|
183
|
+
} catch (error) {
|
|
184
|
+
opts.logFn(`Error loading CRD: ${error.message}`);
|
|
185
|
+
throw new Error(`Failed to read ${opts.source} as a file, URL, or Kubernetes CRD`);
|
|
188
186
|
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Resolves the source file path, treating relative paths as local files.
|
|
191
|
+
*
|
|
192
|
+
* @param source - The source path to resolve.
|
|
193
|
+
* @returns The resolved file path.
|
|
194
|
+
*/
|
|
195
|
+
export function resolveFilePath(source: string): string {
|
|
196
|
+
return source.startsWith("/") ? source : path.join(process.cwd(), source);
|
|
197
|
+
}
|
|
189
198
|
|
|
190
|
-
|
|
199
|
+
/**
|
|
200
|
+
* Tries to parse the source as a URL.
|
|
201
|
+
*
|
|
202
|
+
* @param source - The source string to parse as a URL.
|
|
203
|
+
* @returns The parsed URL object or null if parsing fails.
|
|
204
|
+
*/
|
|
205
|
+
export function tryParseUrl(source: string): URL | null {
|
|
191
206
|
try {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
throw new Error(
|
|
196
|
-
`Failed to read ${source} as a file, url or K8s CRD: ${
|
|
197
|
-
e.data?.message || "Cluster not available"
|
|
198
|
-
}`,
|
|
199
|
-
);
|
|
207
|
+
return new URL(source);
|
|
208
|
+
} catch {
|
|
209
|
+
return null;
|
|
200
210
|
}
|
|
201
211
|
}
|
|
202
212
|
|
|
203
213
|
/**
|
|
204
|
-
*
|
|
214
|
+
* Main generate function to convert CRDs to TypeScript types.
|
|
205
215
|
*
|
|
206
|
-
* @param opts The options
|
|
207
|
-
* @returns A promise that resolves
|
|
216
|
+
* @param opts - The options for generating the TypeScript types.
|
|
217
|
+
* @returns A promise that resolves to a record of generated TypeScript types.
|
|
208
218
|
*/
|
|
209
|
-
export async function generate(opts: GenerateOptions)
|
|
219
|
+
export async function generate(opts: GenerateOptions): Promise<
|
|
220
|
+
{
|
|
221
|
+
results: Record<string, string[]>;
|
|
222
|
+
name: string;
|
|
223
|
+
crd: CustomResourceDefinition;
|
|
224
|
+
version: string;
|
|
225
|
+
}[]
|
|
226
|
+
> {
|
|
210
227
|
const crds = (await readOrFetchCrd(opts)).filter(crd => !!crd);
|
|
211
|
-
const
|
|
228
|
+
const allResults: {
|
|
229
|
+
results: Record<string, string[]>;
|
|
230
|
+
name: string;
|
|
231
|
+
crd: CustomResourceDefinition;
|
|
232
|
+
version: string;
|
|
233
|
+
}[] = [];
|
|
212
234
|
|
|
213
235
|
opts.logFn("");
|
|
214
236
|
|
|
@@ -219,19 +241,16 @@ export async function generate(opts: GenerateOptions) {
|
|
|
219
241
|
continue;
|
|
220
242
|
}
|
|
221
243
|
|
|
222
|
-
|
|
223
|
-
const out = await convertCRDtoTS(crd, opts);
|
|
224
|
-
for (const key of Object.keys(out)) {
|
|
225
|
-
results[key] = out[key];
|
|
226
|
-
}
|
|
244
|
+
allResults.push(...(await convertCRDtoTS(crd, opts)));
|
|
227
245
|
}
|
|
228
246
|
|
|
229
247
|
if (opts.directory) {
|
|
230
248
|
// Notify the user that the files have been generated
|
|
231
|
-
opts.logFn(
|
|
232
|
-
|
|
233
|
-
|
|
249
|
+
opts.logFn(`\n✅ Generated ${allResults.length} files in the ${opts.directory} directory`);
|
|
250
|
+
} else {
|
|
251
|
+
// Log a message about the number of generated files even when no directory is provided
|
|
252
|
+
opts.logFn(`\n✅ Generated ${allResults.length} files`);
|
|
234
253
|
}
|
|
235
254
|
|
|
236
|
-
return
|
|
255
|
+
return allResults;
|
|
237
256
|
}
|