kubernetes-fluent-client 3.5.6 → 3.6.1

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.
Files changed (58) hide show
  1. package/dist/cli.js +11 -16
  2. package/dist/fetch.js +5 -8
  3. package/dist/fluent/index.d.ts +3 -2
  4. package/dist/fluent/index.d.ts.map +1 -1
  5. package/dist/fluent/index.js +27 -29
  6. package/dist/fluent/shared-types.d.ts +49 -0
  7. package/dist/fluent/shared-types.d.ts.map +1 -0
  8. package/dist/fluent/shared-types.js +23 -0
  9. package/dist/fluent/types.d.ts +3 -38
  10. package/dist/fluent/types.d.ts.map +1 -1
  11. package/dist/fluent/types.js +1 -14
  12. package/dist/fluent/utils.d.ts +2 -2
  13. package/dist/fluent/utils.d.ts.map +1 -1
  14. package/dist/fluent/utils.js +25 -35
  15. package/dist/fluent/watch.d.ts +2 -9
  16. package/dist/fluent/watch.d.ts.map +1 -1
  17. package/dist/fluent/watch.js +23 -43
  18. package/dist/generate.d.ts +2 -2
  19. package/dist/generate.d.ts.map +1 -1
  20. package/dist/generate.js +22 -65
  21. package/dist/helpers.js +5 -10
  22. package/dist/index.d.ts +10 -10
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +13 -60
  25. package/dist/kinds.d.ts +1 -1
  26. package/dist/kinds.d.ts.map +1 -1
  27. package/dist/kinds.js +2 -7
  28. package/dist/normalization.d.ts +97 -0
  29. package/dist/normalization.d.ts.map +1 -0
  30. package/dist/normalization.js +152 -0
  31. package/dist/patch.js +1 -2
  32. package/dist/postProcessing.d.ts +8 -129
  33. package/dist/postProcessing.d.ts.map +1 -1
  34. package/dist/postProcessing.js +29 -280
  35. package/dist/types.d.ts +3 -2
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/types.js +1 -5
  38. package/dist/upstream.d.ts +1 -1
  39. package/dist/upstream.d.ts.map +1 -1
  40. package/dist/upstream.js +2 -53
  41. package/package.json +15 -14
  42. package/src/cli.ts +6 -6
  43. package/src/fluent/index.ts +19 -18
  44. package/src/fluent/shared-types.ts +55 -0
  45. package/src/fluent/types.ts +4 -55
  46. package/src/fluent/utils.ts +5 -5
  47. package/src/fluent/watch.ts +11 -24
  48. package/src/generate.ts +4 -4
  49. package/src/index.ts +10 -10
  50. package/src/kinds.ts +1 -1
  51. package/src/normalization.ts +181 -0
  52. package/src/postProcessing.ts +27 -244
  53. package/src/types.ts +3 -4
  54. package/src/upstream.ts +1 -1
  55. package/dist/fileSystem.d.ts +0 -11
  56. package/dist/fileSystem.d.ts.map +0 -1
  57. package/dist/fileSystem.js +0 -52
  58. package/src/fileSystem.ts +0 -25
package/src/cli.ts CHANGED
@@ -5,10 +5,11 @@
5
5
 
6
6
  import { hideBin } from "yargs/helpers";
7
7
  import yargs from "yargs/yargs";
8
- import { GenerateOptions, generate } from "./generate";
9
- import { version } from "../package.json";
10
- import { postProcessing } from "./postProcessing";
11
- import { NodeFileSystem } from "./fileSystem"; // Import your new file system
8
+ import { GenerateOptions, generate } from "./generate.js";
9
+ import { postProcessing } from "./postProcessing.js";
10
+ import { createRequire } from "node:module";
11
+ const require = createRequire(import.meta.url);
12
+ const { version } = require("../package.json");
12
13
 
13
14
  void yargs(hideBin(process.argv))
14
15
  .version("version", "Display version number", `kubernetes-fluent-client v${version}`)
@@ -55,7 +56,6 @@ void yargs(hideBin(process.argv))
55
56
  opts.noPost = argv.noPost as boolean;
56
57
 
57
58
  // Use NodeFileSystem as the file system for post-processing
58
- const fileSystem = new NodeFileSystem(); // Create an instance of NodeFileSystem
59
59
 
60
60
  if (!opts.noPost) {
61
61
  console.log("\n✅ Post-processing has been enabled.\n");
@@ -67,7 +67,7 @@ void yargs(hideBin(process.argv))
67
67
 
68
68
  // If noPost is false, run post-processing
69
69
  if (!opts.noPost) {
70
- await postProcessing(allResults, opts, fileSystem); // Pass the file system to postProcessing
70
+ await postProcessing(allResults, opts); // Pass the file system to postProcessing
71
71
  }
72
72
  } catch (e) {
73
73
  console.log(`\n❌ ${e.message}`);
@@ -6,14 +6,15 @@ import { Operation } from "fast-json-patch";
6
6
  import { StatusCodes } from "http-status-codes";
7
7
  import type { PartialDeep } from "type-fest";
8
8
 
9
- import { fetch } from "../fetch";
10
- import { modelToGroupVersionKind } from "../kinds";
11
- import { GenericClass } from "../types";
12
- import { ApplyCfg, FetchMethods, Filters, K8sInit, Paths, WatchAction } from "./types";
13
- import { k8sCfg, k8sExec } from "./utils";
14
- import { WatchCfg, Watcher } from "./watch";
15
- import { hasLogs } from "../helpers";
16
- import { Pod, type Service, type ReplicaSet } from "../upstream";
9
+ import { fetch } from "../fetch.js";
10
+ import { modelToGroupVersionKind } from "../kinds.js";
11
+ import { GenericClass } from "../types.js";
12
+ import { K8sInit, Paths } from "./types.js";
13
+ import { Filters, WatchAction, FetchMethods, ApplyCfg } from "./shared-types.js";
14
+ import { k8sCfg, k8sExec } from "./utils.js";
15
+ import { WatchCfg, Watcher } from "./watch.js";
16
+ import { hasLogs } from "../helpers.js";
17
+ import { Pod, type Service, type ReplicaSet } from "../upstream.js";
17
18
  /**
18
19
  * Kubernetes fluent API inspired by Kubectl. Pass in a model, then call filters and actions on it.
19
20
  *
@@ -114,7 +115,7 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
114
115
  }
115
116
 
116
117
  try {
117
- const object = await k8sExec<T, K>(model, filters, "GET");
118
+ const object = await k8sExec<T, K>(model, filters, FetchMethods.GET);
118
119
 
119
120
  if (kind !== "Pod") {
120
121
  if (kind === "Service") {
@@ -144,7 +145,7 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
144
145
 
145
146
  const podModel = { ...model, name: "V1Pod" };
146
147
  const logPromises = podList.map(po =>
147
- k8sExec<T, string>(podModel, { ...filters, name: po.metadata!.name! }, "LOG"),
148
+ k8sExec<T, string>(podModel, { ...filters, name: po.metadata!.name! }, FetchMethods.LOG),
148
149
  );
149
150
 
150
151
  const responses = await Promise.all(logPromises);
@@ -179,7 +180,7 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
179
180
  filters.name = name;
180
181
  }
181
182
 
182
- return k8sExec<T, K | KubernetesListObject<K>>(model, filters, "GET");
183
+ return k8sExec<T, K | KubernetesListObject<K>>(model, filters, FetchMethods.GET);
183
184
  }
184
185
 
185
186
  /**
@@ -195,7 +196,7 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
195
196
 
196
197
  try {
197
198
  // Try to delete the resource
198
- await k8sExec<T, void>(model, filters, "DELETE");
199
+ await k8sExec<T, void>(model, filters, FetchMethods.DELETE);
199
200
  } catch (e) {
200
201
  // If the resource doesn't exist, ignore the error
201
202
  if (e.status === StatusCodes.NOT_FOUND) {
@@ -215,7 +216,7 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
215
216
  applyCfg: ApplyCfg = { force: false },
216
217
  ): Promise<K> {
217
218
  syncFilters(resource as K);
218
- return k8sExec(model, filters, "APPLY", resource, applyCfg);
219
+ return k8sExec(model, filters, FetchMethods.APPLY, resource, applyCfg);
219
220
  }
220
221
 
221
222
  /**
@@ -224,7 +225,7 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
224
225
  */
225
226
  async function Create(resource: K): Promise<K> {
226
227
  syncFilters(resource);
227
- return k8sExec(model, filters, "POST", resource);
228
+ return k8sExec(model, filters, FetchMethods.POST, resource);
228
229
  }
229
230
 
230
231
  /**
@@ -248,7 +249,7 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
248
249
  },
249
250
  };
250
251
  // Try to evict the resource
251
- await k8sExec<T, void>(model, filters, "POST", evictionPayload);
252
+ await k8sExec<T, void>(model, filters, FetchMethods.POST, evictionPayload);
252
253
  } catch (e) {
253
254
  // If the resource doesn't exist, ignore the error
254
255
  if (e.status === StatusCodes.NOT_FOUND) {
@@ -268,7 +269,7 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
268
269
  throw new Error("No operations specified");
269
270
  }
270
271
 
271
- return k8sExec(model, filters, "PATCH", payload);
272
+ return k8sExec(model, filters, FetchMethods.PATCH, payload);
272
273
  }
273
274
 
274
275
  /**
@@ -277,7 +278,7 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
277
278
  */
278
279
  async function PatchStatus(resource: PartialDeep<K>): Promise<K> {
279
280
  syncFilters(resource as K);
280
- return k8sExec(model, filters, "PATCH_STATUS", resource);
281
+ return k8sExec(model, filters, FetchMethods.PATCH_STATUS, resource);
281
282
  }
282
283
 
283
284
  /**
@@ -292,7 +293,7 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
292
293
  * @inheritdoc
293
294
  * @see {@link K8sInit.Raw}
294
295
  */
295
- async function Raw(url: string, method: FetchMethods = "GET") {
296
+ async function Raw(url: string, method: FetchMethods = FetchMethods.GET) {
296
297
  const thing = await k8sCfg(method);
297
298
  const { opts, serverUrl } = thing;
298
299
  const resp = await fetch<K>(`${serverUrl}${url}`, opts);
@@ -0,0 +1,55 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Kubernetes Fluent Client Authors
3
+ import { GenericClass, GroupVersionKind } from "../types.js";
4
+ import { RequestInit } from "undici";
5
+ import { KubernetesObject } from "@kubernetes/client-node";
6
+ /**
7
+ * Fetch options and server URL
8
+ */
9
+ export type K8sConfigPromise = Promise<{ opts: RequestInit; serverUrl: string | URL }>;
10
+
11
+ /**
12
+ * The Phase matched when using the K8s Watch API.
13
+ */
14
+ export enum WatchPhase {
15
+ Added = "ADDED",
16
+ Modified = "MODIFIED",
17
+ Deleted = "DELETED",
18
+ Bookmark = "BOOKMARK",
19
+ Error = "ERROR",
20
+ }
21
+
22
+ export type WatchAction<T extends GenericClass, K extends KubernetesObject = InstanceType<T>> = (
23
+ update: K,
24
+ phase: WatchPhase,
25
+ ) => Promise<void> | void;
26
+
27
+ export interface Filters {
28
+ kindOverride?: GroupVersionKind;
29
+ fields?: Record<string, string>;
30
+ labels?: Record<string, string>;
31
+ name?: string;
32
+ namespace?: string;
33
+ }
34
+
35
+ /**
36
+ * Configuration for the apply function.
37
+ */
38
+ export type ApplyCfg = {
39
+ /**
40
+ * Force the apply to be a create.
41
+ */
42
+ force?: boolean;
43
+ };
44
+
45
+ export enum FetchMethods {
46
+ APPLY = "APPLY",
47
+ DELETE = "DELETE",
48
+ GET = "GET",
49
+ LOG = "LOG",
50
+ PATCH = "PATCH",
51
+ PATCH_STATUS = "PATCH_STATUS",
52
+ POST = "POST",
53
+ PUT = "PUT",
54
+ WATCH = "WATCH",
55
+ }
@@ -4,22 +4,16 @@
4
4
  import { KubernetesListObject, KubernetesObject } from "@kubernetes/client-node";
5
5
  import { Operation } from "fast-json-patch";
6
6
  import type { PartialDeep } from "type-fest";
7
- import { RequestInit } from "undici";
8
- import { GenericClass, GroupVersionKind } from "../types";
9
- import { WatchCfg, Watcher } from "./watch";
7
+ import { GenericClass } from "../types.js";
8
+ import { WatchCfg, Watcher } from "./watch.js";
10
9
  import https from "https";
11
10
  import { SecureClientSessionOptions } from "http2";
12
-
11
+ import { WatchAction, FetchMethods, ApplyCfg } from "./shared-types.js";
13
12
  /*
14
- * Watch Class Type
13
+ * Watch Class Type - Used in Pepr Watch Processor
15
14
  */
16
15
  export type WatcherType<T extends GenericClass> = Watcher<T>;
17
16
 
18
- /**
19
- * Fetch options and server URL
20
- */
21
- export type K8sConfigPromise = Promise<{ opts: RequestInit; serverUrl: string | URL }>;
22
-
23
17
  /**
24
18
  * Agent options for the the http2Watch
25
19
  */
@@ -35,36 +29,6 @@ export interface Options {
35
29
  agent?: https.Agent & { options?: AgentOptions };
36
30
  }
37
31
 
38
- /**
39
- * The Phase matched when using the K8s Watch API.
40
- */
41
- export enum WatchPhase {
42
- Added = "ADDED",
43
- Modified = "MODIFIED",
44
- Deleted = "DELETED",
45
- Bookmark = "BOOKMARK",
46
- Error = "ERROR",
47
- }
48
-
49
- export type FetchMethods =
50
- | "APPLY"
51
- | "DELETE"
52
- | "GET"
53
- | "LOG"
54
- | "PATCH"
55
- | "PATCH_STATUS"
56
- | "POST"
57
- | "PUT"
58
- | "WATCH";
59
-
60
- export interface Filters {
61
- kindOverride?: GroupVersionKind;
62
- fields?: Record<string, string>;
63
- labels?: Record<string, string>;
64
- name?: string;
65
- namespace?: string;
66
- }
67
-
68
32
  /**
69
33
  * Get the resource or resources matching the filters.
70
34
  * If no filters are specified, all resources will be returned.
@@ -225,16 +189,6 @@ export type K8sWithFilters<T extends GenericClass, K extends KubernetesObject> =
225
189
  WithLabel: (key: string, value?: string) => K8sWithFilters<T, K>;
226
190
  };
227
191
 
228
- /**
229
- * Configuration for the apply function.
230
- */
231
- export type ApplyCfg = {
232
- /**
233
- * Force the apply to be a create.
234
- */
235
- force?: boolean;
236
- };
237
-
238
192
  export type K8sInit<T extends GenericClass, K extends KubernetesObject> = K8sWithFilters<T, K> &
239
193
  K8sUnfilteredActions<K> & {
240
194
  /**
@@ -246,11 +200,6 @@ export type K8sInit<T extends GenericClass, K extends KubernetesObject> = K8sWit
246
200
  InNamespace: (namespace: string) => K8sWithFilters<T, K>;
247
201
  };
248
202
 
249
- export type WatchAction<T extends GenericClass, K extends KubernetesObject = InstanceType<T>> = (
250
- update: K,
251
- phase: WatchPhase,
252
- ) => Promise<void> | void;
253
-
254
203
  // Special types to handle the recursive keyof typescript lookup
255
204
  type Join<K, P> = K extends string | number
256
205
  ? P extends string | number
@@ -6,10 +6,10 @@ import { RequestInit } from "node-fetch";
6
6
  import { URL } from "url";
7
7
  import { Agent, Dispatcher } from "undici";
8
8
  import { Agent as httpsAgent } from "https";
9
- import { fetch } from "../fetch";
10
- import { modelToGroupVersionKind } from "../kinds";
11
- import { GenericClass } from "../types";
12
- import { ApplyCfg, FetchMethods, Filters, K8sConfigPromise } from "./types";
9
+ import { fetch } from "../fetch.js";
10
+ import { modelToGroupVersionKind } from "../kinds.js";
11
+ import { GenericClass } from "../types.js";
12
+ import { ApplyCfg, Filters, K8sConfigPromise, FetchMethods } from "./shared-types.js";
13
13
  import fs from "fs";
14
14
  import { V1Eviction as Eviction } from "@kubernetes/client-node";
15
15
  const SSA_CONTENT_TYPE = "application/apply-patch+yaml";
@@ -222,7 +222,7 @@ export async function k8sExec<T extends GenericClass, K>(
222
222
  applyCfg: ApplyCfg = { force: false },
223
223
  ) {
224
224
  const reconstruct = async (method: FetchMethods): K8sConfigPromise => {
225
- const configMethod = method === "LOG" ? "GET" : method;
225
+ const configMethod = method === FetchMethods.LOG ? FetchMethods.GET : method;
226
226
  const { opts, serverUrl } = await k8sCfg(configMethod);
227
227
 
228
228
  // Build the base path once, using excludeName only for standard POST requests
@@ -1,15 +1,19 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Kubernetes Fluent Client Authors
3
3
 
4
- import { createHash } from "crypto";
5
4
  import { EventEmitter } from "events";
6
5
  import { fetch } from "undici";
7
- import { fetch as wrappedFetch } from "../fetch";
8
- import { GenericClass, KubernetesListObject } from "../types";
9
- import { Filters, WatchAction, WatchPhase, K8sConfigPromise } from "./types";
10
- import { k8sCfg, pathBuilder, getHeaders } from "./utils";
6
+ import { fetch as wrappedFetch } from "../fetch.js";
7
+ import { GenericClass, KubernetesListObject } from "../types.js";
8
+ import { k8sCfg, pathBuilder, getHeaders } from "./utils.js";
11
9
  import { Readable } from "stream";
12
-
10
+ import {
11
+ K8sConfigPromise,
12
+ WatchPhase,
13
+ WatchAction,
14
+ Filters,
15
+ FetchMethods,
16
+ } from "./shared-types.js";
13
17
  export enum WatchEvent {
14
18
  /** Watch is connected successfully */
15
19
  CONNECT = "connect",
@@ -171,23 +175,6 @@ export class Watcher<T extends GenericClass> {
171
175
  this.#abortController.abort();
172
176
  }
173
177
 
174
- /**
175
- * Get a unique ID for the watch based on the model and filters.
176
- * This is useful for caching the watch data or resource versions.
177
- *
178
- * @returns the watch CacheID
179
- */
180
- public getCacheID() {
181
- // Build the URL, we don't care about the server URL or resourceVersion
182
- const url = pathBuilder("https://ignore", this.#model, this.#filters, false);
183
-
184
- // Hash and truncate the ID to 10 characters, cache the result
185
- return createHash("sha224")
186
- .update(url.pathname + url.search)
187
- .digest("hex")
188
- .substring(0, 10);
189
- }
190
-
191
178
  /**
192
179
  * Subscribe to watch events. This is an EventEmitter that emits the following events:
193
180
  *
@@ -214,7 +201,7 @@ export class Watcher<T extends GenericClass> {
214
201
  continueToken?: string,
215
202
  ): K8sConfigPromise => {
216
203
  // Build the path and query params for the resource, excluding the name
217
- const { opts, serverUrl } = await k8sCfg("GET");
204
+ const { opts, serverUrl } = await k8sCfg(FetchMethods.GET);
218
205
  const k8sUrl = serverUrl instanceof URL ? serverUrl.toString() : serverUrl;
219
206
  const url = pathBuilder(k8sUrl, this.#model, this.#filters, true);
220
207
 
package/src/generate.ts CHANGED
@@ -12,10 +12,10 @@ import {
12
12
  quicktype,
13
13
  } from "quicktype-core";
14
14
 
15
- import { fetch } from "./fetch";
16
- import { K8s } from "./fluent";
17
- import { CustomResourceDefinition } from "./upstream";
18
- import { LogFn } from "./types";
15
+ import { fetch } from "./fetch.js";
16
+ import { K8s } from "./fluent/index.js";
17
+ import { CustomResourceDefinition } from "./upstream.js";
18
+ import { LogFn } from "./types.js";
19
19
 
20
20
  export interface GenerateOptions {
21
21
  source: string; // URL, file path, or K8s CRD name
package/src/index.ts CHANGED
@@ -1,35 +1,35 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Kubernetes Fluent Client Authors
3
3
 
4
- import "./patch";
4
+ import "./patch.js";
5
5
 
6
6
  // Export kinds as a single object
7
- import * as kind from "./upstream";
7
+ import * as kind from "./upstream.js";
8
8
 
9
9
  /** kind is a collection of K8s types to be used within a K8s call: `K8s(kind.Secret).Apply({})`. */
10
10
  export { kind };
11
11
 
12
12
  // Export the node-fetch wrapper
13
- export { fetch } from "./fetch";
13
+ export { fetch } from "./fetch.js";
14
14
 
15
15
  // Export the HTTP status codes
16
16
  export { StatusCodes as fetchStatus } from "http-status-codes";
17
17
 
18
18
  // Export the Watch Config and Event types
19
- export { WatchCfg, WatchEvent } from "./fluent/watch";
19
+ export { WatchCfg, WatchEvent } from "./fluent/watch.js";
20
20
 
21
21
  // Export the fluent API entrypoint
22
- export { K8s } from "./fluent";
22
+ export { K8s } from "./fluent/index.js";
23
23
 
24
24
  // Export helpers for working with K8s types
25
- export { RegisterKind, modelToGroupVersionKind } from "./kinds";
25
+ export { RegisterKind, modelToGroupVersionKind } from "./kinds.js";
26
26
 
27
27
  // Export the GenericKind interface for CRD registration
28
- export { GenericKind } from "./types";
28
+ export { GenericKind } from "./types.js";
29
29
 
30
- export * from "./types";
30
+ export * from "./types.js";
31
31
 
32
32
  // Export the upstream raw models
33
- export * as models from "@kubernetes/client-node/dist/gen/models/all";
33
+ export * as models from "@kubernetes/client-node/dist/gen/models/all.js";
34
34
 
35
- export { fromEnv, waitForCluster } from "./helpers";
35
+ export { fromEnv, waitForCluster } from "./helpers.js";
package/src/kinds.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Kubernetes Fluent Client Authors
3
3
 
4
- import { GenericClass, GroupVersionKind } from "./types";
4
+ import { GenericClass, GroupVersionKind } from "./types.js";
5
5
 
6
6
  const gvkMap: Record<string, GroupVersionKind> = {
7
7
  /**
@@ -0,0 +1,181 @@
1
+ import { GenerateOptions } from "./generate.js";
2
+
3
+ /**
4
+ * Normalizes indentation for TypeScript lines to a consistent format.
5
+ *
6
+ * @param lines The generated TypeScript lines.
7
+ * @returns The lines with normalized indentation.
8
+ */
9
+ export function normalizeIndentation(lines: string[]): string[] {
10
+ return lines.map(line => line.replace(/^ {4}/, " "));
11
+ }
12
+
13
+ /**
14
+ * Normalizes the indentation of a single line to use two spaces instead of four.
15
+ *
16
+ * @param line The line of code to normalize.
17
+ * @returns The line with normalized indentation.
18
+ */
19
+ export function normalizeLineIndentation(line: string): string {
20
+ return line.replace(/^ {4}/, " ");
21
+ }
22
+
23
+ /**
24
+ * Normalizes spacing between property names and types in TypeScript lines.
25
+ *
26
+ * @param lines The generated TypeScript lines.
27
+ * @returns The lines with normalized property spacing.
28
+ */
29
+ export function normalizePropertySpacing(lines: string[]): string[] {
30
+ // https://regex101.com/r/XEv3pL/1
31
+ return lines.map(line => line.replace(/\s*\?\s*:\s*/, "?: "));
32
+ }
33
+
34
+ /**
35
+ * Processes a single line inside a class extending `GenericKind`.
36
+ *
37
+ * @param line The current line of code.
38
+ * @param genericKindProperties The list of properties from `GenericKind`.
39
+ * @param foundInterfaces The set of found interfaces in the file.
40
+ * @returns The modified line.
41
+ */
42
+ export function modifyAndNormalizeClassProperties(
43
+ line: string,
44
+ genericKindProperties: string[],
45
+ foundInterfaces: Set<string>,
46
+ ): string {
47
+ line = modifyPropertiesAndAddEslintDirective(line, genericKindProperties, foundInterfaces);
48
+ line = normalizeLineIndentation(line);
49
+ return line;
50
+ }
51
+
52
+ /**
53
+ * Normalizes lines after processing, including indentation, spacing, and removing unnecessary lines.
54
+ *
55
+ * @param lines The lines of the file content.
56
+ * @param opts The options for processing.
57
+ * @returns The normalized lines.
58
+ */
59
+ export function normalizeIndentationAndSpacing(lines: string[], opts: GenerateOptions): string[] {
60
+ let normalizedLines = normalizeIndentation(lines);
61
+ normalizedLines = normalizePropertySpacing(normalizedLines);
62
+ return removePropertyStringAny(normalizedLines, opts);
63
+ }
64
+
65
+ /**
66
+ * Removes lines containing `[property: string]: any;` from TypeScript files.
67
+ *
68
+ * @param lines The generated TypeScript lines.
69
+ * @param opts The options for processing.
70
+ * @returns The lines with `[property: string]: any;` removed.
71
+ */
72
+ export function removePropertyStringAny(lines: string[], opts: GenerateOptions): string[] {
73
+ if (opts.language === "ts" || opts.language === "typescript") {
74
+ return lines.filter(line => !line.includes("[property: string]: any;"));
75
+ }
76
+ return lines;
77
+ }
78
+
79
+ /**
80
+ * Applies ESLint and property modifiers to a line of code.
81
+ *
82
+ * @param line - The current line of code.
83
+ * @param genericKindProperties - The list of properties from `GenericKind`.
84
+ * @param foundInterfaces - The set of found interfaces in the file.
85
+ * @returns The modified line.
86
+ */
87
+ export function modifyPropertiesAndAddEslintDirective(
88
+ line: string,
89
+ genericKindProperties: string[],
90
+ foundInterfaces: Set<string>,
91
+ ): string {
92
+ line = addDeclareAndOptionalModifiersToProperties(line, genericKindProperties, foundInterfaces);
93
+ line = processEslintDisable(line, genericKindProperties);
94
+ return line;
95
+ }
96
+
97
+ /**
98
+ * Adds an ESLint disable comment for `[key: string]: any` if it's not part of `GenericKind`.
99
+ *
100
+ * @param line The current line of code.
101
+ * @param genericKindProperties The list of properties from `GenericKind`.
102
+ * @returns The modified line with the ESLint disable comment.
103
+ */
104
+ export function processEslintDisable(line: string, genericKindProperties: string[]): string {
105
+ if (line.includes("[key: string]: any") && !genericKindProperties.includes("[key: string]")) {
106
+ return ` // eslint-disable-next-line @typescript-eslint/no-explicit-any\n${line}`;
107
+ }
108
+ return line;
109
+ }
110
+
111
+ /**
112
+ * Applies property modifiers to a line of code.
113
+ *
114
+ * @param line The current line of code.
115
+ * @param genericKindProperties The list of properties from `GenericKind`.
116
+ * @param foundInterfaces The set of found interfaces in the file.
117
+ * @returns The modified line.
118
+ */
119
+ export function addDeclareAndOptionalModifiersToProperties(
120
+ line: string,
121
+ genericKindProperties: string[],
122
+ foundInterfaces: Set<string>,
123
+ ): string {
124
+ line = addDeclareToGenericKindProperties(line, genericKindProperties);
125
+ line = makePropertiesOptional(line, foundInterfaces);
126
+ line = normalizeLineIndentation(line);
127
+ return line;
128
+ }
129
+
130
+ /**
131
+ * Adds the `declare` keyword to `GenericKind` properties.
132
+ *
133
+ * @param line The current line of code.
134
+ * @param genericKindProperties The list of properties from `GenericKind`.
135
+ * @returns The modified line with the `declare` keyword, if applicable.
136
+ */
137
+ export function addDeclareToGenericKindProperties(
138
+ line: string,
139
+ genericKindProperties: string[],
140
+ ): string {
141
+ for (const prop of genericKindProperties) {
142
+ const propertyPattern = getPropertyPattern(prop);
143
+ if (propertyPattern.test(line)) {
144
+ return line.replace(prop, `declare ${prop}`);
145
+ }
146
+ }
147
+ return line;
148
+ }
149
+
150
+ /**
151
+ * Makes a property optional if its type matches one of the found interfaces and it is not already optional.
152
+ *
153
+ * @param line The current line of code.
154
+ * @param foundInterfaces The set of found interfaces in the file.
155
+ * @returns The modified line with the optional `?` symbol.
156
+ */
157
+ export function makePropertiesOptional(line: string, foundInterfaces: Set<string>): string {
158
+ // https://regex101.com/r/kX8TCj/1
159
+ const propertyTypePattern = /:\s*(?<propertyType>\w+)\s*;/;
160
+ const match = line.match(propertyTypePattern);
161
+
162
+ if (match?.groups?.propertyType) {
163
+ const { propertyType } = match.groups;
164
+ if (foundInterfaces.has(propertyType) && !line.includes("?")) {
165
+ return line.replace(":", "?:");
166
+ }
167
+ }
168
+ return line;
169
+ }
170
+
171
+ /**
172
+ * Generates a regular expression to match a property pattern in TypeScript.
173
+ *
174
+ * @param prop The property name to match.
175
+ * @returns A regular expression to match the property pattern.
176
+ */
177
+ export function getPropertyPattern(prop: string): RegExp {
178
+ // For prop="kind", the pattern will match "kind ? :" or "kind :"
179
+ // https://regex101.com/r/mF8kXn/1
180
+ return new RegExp(`\\b${prop}\\b\\s*\\?\\s*:|\\b${prop}\\b\\s*:`);
181
+ }