pepr 0.1.25 → 0.1.27

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/pepr-cli.js CHANGED
@@ -13,12 +13,12 @@ var zlib = require('zlib');
13
13
  var forge = require('node-forge');
14
14
  var prompt = require('prompts');
15
15
  var child_process = require('child_process');
16
+ var chokidar = require('chokidar');
16
17
  var util = require('util');
17
18
  var uuid = require('uuid');
18
19
  var commander = require('commander');
19
- var chokidar = require('chokidar');
20
20
 
21
- var version = "0.1.25";
21
+ var version = "0.1.27";
22
22
 
23
23
  // SPDX-License-Identifier: Apache-2.0
24
24
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
@@ -806,9 +806,9 @@ function dev (program) {
806
806
  process.exit(0);
807
807
  }
808
808
  // Build the module
809
- const { cfg, path } = await buildModule(opts.dir);
809
+ const { cfg, path: path$1 } = await buildModule(opts.dir);
810
810
  // Read the compiled module code
811
- const code = await fs.promises.readFile(path, { encoding: "utf-8" });
811
+ const code = await fs.promises.readFile(path$1, { encoding: "utf-8" });
812
812
  // Generate a secret for the module
813
813
  const webhook = new Webhook({
814
814
  ...cfg.pepr,
@@ -820,6 +820,25 @@ function dev (program) {
820
820
  try {
821
821
  await webhook.deploy(code);
822
822
  types.logger.info(`Module deployed successfully`);
823
+ const moduleFiles = path.resolve(opts.dir, "**", "*.ts");
824
+ const watcher = chokidar.watch(moduleFiles);
825
+ const peprTS = path.resolve(opts.dir, "pepr.ts");
826
+ let program;
827
+ // Run the module once to start the server
828
+ runDev(peprTS);
829
+ // Watch for changes
830
+ watcher.on("ready", () => {
831
+ types.logger.info(`Watching for changes in ${moduleFiles}`);
832
+ watcher.on("all", async (event, path) => {
833
+ types.logger.debug({ event, path }, "File changed");
834
+ // Kill the running process
835
+ if (program) {
836
+ program.kill("SIGKILL");
837
+ }
838
+ // Start the process again
839
+ program = runDev(peprTS);
840
+ });
841
+ });
823
842
  }
824
843
  catch (e) {
825
844
  types.logger.error(`Error deploying module: ${e}`);
@@ -827,6 +846,28 @@ function dev (program) {
827
846
  }
828
847
  });
829
848
  }
849
+ function runDev(path) {
850
+ try {
851
+ const program = child_process.spawn("./node_modules/.bin/ts-node", [path], {
852
+ env: {
853
+ ...process.env,
854
+ SSL_KEY_PATH: "insecure-tls.key",
855
+ SSL_CERT_PATH: "insecure-tls.crt",
856
+ },
857
+ });
858
+ program.stdout.on("data", data => console.log(data.toString()));
859
+ program.stderr.on("data", data => console.error(data.toString()));
860
+ program.on("close", code => {
861
+ types.logger.info(`Process exited with code ${code}`);
862
+ });
863
+ return program;
864
+ }
865
+ catch (e) {
866
+ types.logger.debug(e);
867
+ types.logger.error(`Error running module: ${e}`);
868
+ process.exit(1);
869
+ }
870
+ }
830
871
 
831
872
  // SPDX-License-Identifier: Apache-2.0
832
873
  /**
@@ -883,28 +924,18 @@ function genPeprTS() {
883
924
  path: "pepr.ts",
884
925
  data: `import { PeprModule } from "pepr";
885
926
  import cfg from "./package.json";
886
- // import { HelloPepr } from "./capabilities/hello-pepr";
887
-
888
- // This initializes the Pepr module with the configuration from package.json
889
- export const { ProcessRequest, Register } = new PeprModule(cfg);
927
+ import { HelloPepr } from "./capabilities/hello-pepr";
890
928
 
891
929
  /**
892
- * Each Pepr Capability is registered with the module using the Register function.
893
- * This will be automatically generated by the Pepr CLI when creating a new capability:
894
- * \`pepr new <capability name>\`
895
- *
896
- * Example:
897
- * import { Capability1 } from "./capabilities/capability1";
898
- * import { Capability2 } from "./capabilities/capability2";
899
- *
900
- * Capability1(Register);
901
- * Capability2(Register);
902
- *
903
- * Uncomment the line below and the import above to use the hello-pepr demo capability
930
+ * This is the main entrypoint for the Pepr module. It is the file that is run when the module is started.
931
+ * This is where you register your configurations and capabilities with the module.
904
932
  */
933
+ new PeprModule(cfg, [
934
+ // "HelloPepr" is a demo capability that is included with Pepr. You can remove it if you want.
935
+ HelloPepr,
905
936
 
906
- // HelloPepr(Register);
907
-
937
+ // Your additional capabilities go here
938
+ ]);
908
939
  `,
909
940
  };
910
941
  }
@@ -1009,14 +1040,15 @@ const capabilityHelloPeprTS = {
1009
1040
  path: "hello-pepr.ts",
1010
1041
  data: `import { Capability, a } from "pepr";
1011
1042
 
1012
- const { Register, When } = new Capability({
1043
+ export const HelloPepr = new Capability({
1013
1044
  name: "hello-pepr",
1014
1045
  description: "A simple example capability to show how things work.",
1015
1046
  namespaces: ["pepr-demo"],
1016
1047
  });
1017
1048
 
1018
- export { Register as HelloPepr };
1019
-
1049
+ // Use the 'When' function to create a new Capability Action
1050
+ const { When } = HelloPepr;
1051
+
1020
1052
  /**
1021
1053
  * This is a single Capability Action. They can be in the same file or put imported from other files.
1022
1054
  * In this exmaple, when a ConfigMap is created with the name \`example-1\`, then add a label and annotation.
@@ -1079,27 +1111,26 @@ When(a.ConfigMap)
1079
1111
  const capabilitySnippet = {
1080
1112
  path: "pepr.code-snippets",
1081
1113
  data: `{
1082
- "Create a new Pepr capability": {
1083
- "prefix": "create pepr capability",
1084
- "body": [
1085
- "import { Capability, a } from 'pepr';",
1086
- "",
1087
- "const { Register, When } = new Capability({",
1088
- "\tname: '$\{TM_FILENAME_BASE}',",
1089
- "\tdescription: '$\{1:A brief description of this capability.}',",
1090
- "\tnamespaces: [$\{2:}],",
1091
- "});",
1092
- "",
1093
- "// Export the capability so it can be imported in the pepr.ts file.",
1094
- "export { Register as $\{TM_FILENAME_BASE/(.*)/$\{1:/pascalcase}/} };",
1095
- "",
1096
- "// When(a.<Kind>).Is<Event>().Then(change => change.<changes>",
1097
- "When($\{3:})"
1098
- ],
1099
- "description": "Creates a new Pepr capability with a specified description, and optional namespaces, and adds a When statement for the specified value."
1100
- }
1114
+ "Create a new Pepr capability": {
1115
+ "prefix": "create pepr capability",
1116
+ "body": [
1117
+ "import { Capability, a } from 'pepr';",
1118
+ "",
1119
+ "export const $\{TM_FILENAME_BASE/(.*)/$\{1:/pascalcase}/} = new Capability({",
1120
+ "\\tname: '$\{TM_FILENAME_BASE}',",
1121
+ "\\tdescription: '$\{1:A brief description of this capability.}',",
1122
+ "\\tnamespaces: [$\{2:}],",
1123
+ "});",
1124
+ "",
1125
+ "// Use the 'When' function to create a new Capability Action",
1126
+ "const { When } = $\{TM_FILENAME_BASE/(.*)/$\{1:/pascalcase}/};",
1127
+ "",
1128
+ "// When(a.<Kind>).Is<Event>().Then(change => change.<changes>",
1129
+ "When($\{3:})"
1130
+ ],
1131
+ "description": "Creates a new Pepr capability with a specified description, and optional namespaces, and adds a When statement for the specified value."
1101
1132
  }
1102
- `,
1133
+ }`,
1103
1134
  };
1104
1135
 
1105
1136
  // SPDX-License-Identifier: Apache-2.0
package/dist/pepr-core.js CHANGED
@@ -4,6 +4,9 @@
4
4
  var dist = require('@kubernetes/client-node/dist');
5
5
  var types = require('./types-1709b44f.js');
6
6
  var R = require('ramda');
7
+ var express = require('express');
8
+ var fs = require('fs');
9
+ var https = require('https');
7
10
  var fastJsonPatch = require('fast-json-patch');
8
11
 
9
12
  function _interopNamespaceDefault(e) {
@@ -39,7 +42,6 @@ var upstream = /*#__PURE__*/Object.freeze({
39
42
  CustomResourceDefinition: dist.V1CustomResourceDefinition,
40
43
  DaemonSet: dist.V1DaemonSet,
41
44
  Deployment: dist.V1Deployment,
42
- Endpoint: dist.V1Endpoint,
43
45
  EndpointSlice: dist.V1EndpointSlice,
44
46
  HorizontalPodAutoscaler: dist.V1HorizontalPodAutoscaler,
45
47
  Ingress: dist.V1Ingress,
@@ -535,13 +537,6 @@ class Capability {
535
537
  // Currently everything is considered a mutation
536
538
  this._mutateOrValidate = types.HookPhase.mutate;
537
539
  this._bindings = [];
538
- /**
539
- * The Register method is used to register a capability with the Pepr runtime. This method is
540
- * called in the order that the capabilities should be executed.
541
- *
542
- * @param callback the state register method to call, passing the capability as an argument
543
- */
544
- this.Register = (register) => register(this);
545
540
  /**
546
541
  * The When method is used to register a capability action to be executed when a Kubernetes resource is
547
542
  * processed by Pepr. The action will be executed if the resource matches the specified kind and any
@@ -640,19 +635,16 @@ function shouldSkipRequest(binding, req) {
640
635
  const { namespaces, labels, annotations } = binding.filters;
641
636
  const { metadata } = req.object;
642
637
  if (kind !== req.kind.kind) {
643
- types.logger.debug(`${req.kind.kind} does not match ${kind}`);
644
638
  return true;
645
639
  }
646
640
  if (group && group !== req.kind.group) {
647
- types.logger.debug(`${req.kind.group} does not match ${group}`);
648
641
  return true;
649
642
  }
650
643
  if (version && version !== req.kind.version) {
651
- types.logger.debug(`${req.kind.version} does not match ${version}`);
652
644
  return true;
653
645
  }
654
646
  if (namespaces.length && !namespaces.includes(req.namespace || "")) {
655
- types.logger.debug(`${req.namespace} is not in ${namespaces}`);
647
+ types.logger.debug("Namespace does not match");
656
648
  return true;
657
649
  }
658
650
  for (const [key, value] of Object.entries(labels)) {
@@ -848,38 +840,106 @@ function processor(config, capabilities, req) {
848
840
  return response;
849
841
  }
850
842
 
843
+ // SPDX-License-Identifier: Apache-2.0
844
+ // Load SSL certificate and key
845
+ const options = {
846
+ key: fs.readFileSync(process.env.SSL_KEY_PATH || "/etc/certs/tls.key"),
847
+ cert: fs.readFileSync(process.env.SSL_CERT_PATH || "/etc/certs/tls.crt"),
848
+ };
849
+ class Controller {
850
+ constructor(config, capabilities) {
851
+ this.config = config;
852
+ this.capabilities = capabilities;
853
+ this.app = express();
854
+ /** Start the webhook server */
855
+ this.startServer = (port) => {
856
+ // Create HTTPS server
857
+ https.createServer(options, this.app).listen(port, () => {
858
+ console.log(`Server listening on port ${port}`);
859
+ });
860
+ };
861
+ this.logger = (req, res, next) => {
862
+ const startTime = Date.now();
863
+ res.on("finish", () => {
864
+ const now = new Date().toISOString();
865
+ const elapsedTime = Date.now() - startTime;
866
+ const message = `[${now}] ${req.method} ${req.originalUrl} - ${res.statusCode} - ${elapsedTime} ms\n`;
867
+ res.statusCode >= 400 ? console.error(message) : console.info(message);
868
+ });
869
+ next();
870
+ };
871
+ this.healthz = (req, res) => {
872
+ try {
873
+ res.send("OK");
874
+ }
875
+ catch (err) {
876
+ console.error(err);
877
+ res.status(500).send("Internal Server Error");
878
+ }
879
+ };
880
+ this.mutate = (req, res) => {
881
+ try {
882
+ const name = req.body?.request?.name || "";
883
+ const namespace = req.body?.request?.namespace || "";
884
+ const gvk = req.body?.request?.kind || { group: "", version: "", kind: "" };
885
+ console.log(`Mutate request: ${gvk.group}/${gvk.version}/${gvk.kind}`);
886
+ name && console.log(` ${namespace}/${name}\n`);
887
+ // @todo: make this actually do something
888
+ const response = processor(this.config, this.capabilities, req.body.request);
889
+ console.debug(response);
890
+ // Send a no prob bob response
891
+ res.send({
892
+ apiVersion: "admission.k8s.io/v1",
893
+ kind: "AdmissionReview",
894
+ response: {
895
+ uid: req.body.request.uid,
896
+ allowed: true,
897
+ },
898
+ });
899
+ }
900
+ catch (err) {
901
+ console.error(err);
902
+ res.status(500).send("Internal Server Error");
903
+ }
904
+ };
905
+ // Middleware for logging requests
906
+ this.app.use(this.logger);
907
+ // Middleware for parsing JSON
908
+ this.app.use(express.json());
909
+ // Health check endpoint
910
+ this.app.get("/healthz", this.healthz);
911
+ // Mutate endpoint
912
+ this.app.post("/mutate", this.mutate);
913
+ }
914
+ }
915
+
851
916
  // SPDX-License-Identifier: Apache-2.0
852
917
  const alwaysIgnore = {
853
918
  namespaces: ["kube-system", "pepr-system"],
854
919
  labels: [{ "pepr.dev": "ignore" }],
855
920
  };
856
921
  class PeprModule {
857
- get kinds() {
858
- return this._kinds;
859
- }
860
- get UUID() {
861
- return this._config.uuid;
862
- }
863
922
  /**
864
923
  * Create a new Pepr runtime
865
924
  *
866
925
  * @param config The configuration for the Pepr runtime
867
926
  */
868
- constructor({ description, pepr }) {
869
- this._state = [];
870
- this._kinds = [];
871
- this.Register = (capability) => {
872
- types.logger.info(`Registering capability ${capability.name}`);
873
- // Add the kinds to the list of kinds (ignoring duplicates for now)
874
- this._kinds = capability.bindings.map(({ kind }) => kind);
875
- // Add the capability to the state
876
- this._state.push(capability);
877
- };
878
- this.ProcessRequest = (req) => {
879
- return processor(this._config, this._state, req);
880
- };
881
- pepr.description = description;
882
- this._config = R.mergeDeepWith(R.concat, pepr, alwaysIgnore);
927
+ constructor({ description, pepr }, capabilities = [], deferStart = false) {
928
+ const config = R.mergeDeepWith(R.concat, pepr, alwaysIgnore);
929
+ config.description = description;
930
+ this._controller = new Controller(config, capabilities);
931
+ if (!deferStart) {
932
+ this.start();
933
+ }
934
+ }
935
+ /**
936
+ * Start the Pepr runtime manually.
937
+ * Normally this is called automatically when the Pepr module is instantiated, but can be called manually if `deferStart` is set to `true` in the constructor.
938
+ *
939
+ * @param port
940
+ */
941
+ start(port = 3000) {
942
+ this._controller.startServer(port);
883
943
  }
884
944
  }
885
945
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pepr",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "description": "Kubernetes application engine",
5
5
  "author": "Defense Unicorns",
6
6
  "homepage": "https://github.com/defenseunicorns/pepr",
@@ -41,7 +41,6 @@
41
41
  "@rollup/plugin-node-resolve": "^15.0.1",
42
42
  "@rollup/plugin-typescript": "^11.0.0",
43
43
  "@types/ramda": "^0.28.23",
44
- "body-parser": "^1.20.2",
45
44
  "chokidar": "^3.5.3",
46
45
  "commander": "^10.0.0",
47
46
  "express": "^4.18.2",
package/tsconfig.json CHANGED
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "baseUrl": ".",
4
+ "declaration": true,
5
+ "declarationDir": "dist",
4
6
  "esModuleInterop": true,
5
7
  "lib": ["ES2020"],
6
8
  "moduleResolution": "node",
package/CODEOWNERS DELETED
@@ -1,6 +0,0 @@
1
- * @jeff-mccoy @bdw617
2
-
3
- # Additional privileged files
4
- /CODEOWNERS @jeff-mccoy @runyontr
5
- /cosign.pub @jeff-mccoy @runyontr
6
- /LICENSE @jeff-mccoy @runyontr
package/index.ts DELETED
@@ -1,7 +0,0 @@
1
- import { Capability } from './src/lib/capability';
2
- import { a } from './src/lib/k8s';
3
- import Log from './src/lib/logger';
4
- import { PeprModule } from './src/lib/module';
5
-
6
- export { a, PeprModule, Capability, Log };
7
-
@@ -1,158 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
-
4
- import { modelToGroupVersionKind } from "./k8s";
5
- import logger from "./logger";
6
- import {
7
- BindToAction,
8
- Binding,
9
- BindingFilter,
10
- BindingWithName,
11
- CapabilityAction,
12
- CapabilityCfg,
13
- DeepPartial,
14
- Event,
15
- GenericClass,
16
- HookPhase,
17
- WhenSelector,
18
- } from "./types";
19
-
20
- /**
21
- * A capability is a unit of functionality that can be registered with the Pepr runtime.
22
- */
23
- export class Capability implements CapabilityCfg {
24
- private _name: string;
25
- private _description: string;
26
- private _namespaces?: string[] | undefined;
27
-
28
- // Currently everything is considered a mutation
29
- private _mutateOrValidate = HookPhase.mutate;
30
-
31
- private _bindings: Binding[] = [];
32
-
33
- get bindings(): Binding[] {
34
- return this._bindings;
35
- }
36
-
37
- get name() {
38
- return this._name;
39
- }
40
-
41
- get description() {
42
- return this._description;
43
- }
44
-
45
- get namespaces() {
46
- return this._namespaces || [];
47
- }
48
-
49
- get mutateOrValidate() {
50
- return this._mutateOrValidate;
51
- }
52
-
53
- constructor(cfg: CapabilityCfg) {
54
- this._name = cfg.name;
55
- this._description = cfg.description;
56
- this._namespaces = cfg.namespaces;
57
- logger.info(`Capability ${this._name} registered`);
58
- logger.debug(cfg);
59
- }
60
-
61
- /**
62
- * The Register method is used to register a capability with the Pepr runtime. This method is
63
- * called in the order that the capabilities should be executed.
64
- *
65
- * @param callback the state register method to call, passing the capability as an argument
66
- */
67
- Register = (register: (capability: Capability) => void) => register(this);
68
-
69
- /**
70
- * The When method is used to register a capability action to be executed when a Kubernetes resource is
71
- * processed by Pepr. The action will be executed if the resource matches the specified kind and any
72
- * filters that are applied.
73
- *
74
- * @param model if using a custom KubernetesObject not available in `a.*`, specify the GroupVersionKind
75
- * @returns
76
- */
77
- When = <T extends GenericClass>(model: T): WhenSelector<T> => {
78
- const binding: Binding = {
79
- // If the kind is not specified, use the default KubernetesObject
80
- kind: modelToGroupVersionKind(model.name),
81
- filters: {
82
- name: "",
83
- namespaces: [],
84
- labels: {},
85
- annotations: {},
86
- },
87
- callback: () => null,
88
- };
89
-
90
- const prefix = `${this._name}: ${model.name}`;
91
-
92
- logger.info(`Binding created`, prefix);
93
-
94
- const Then = (cb: CapabilityAction<T>): BindToAction<T> => {
95
- logger.info(`Binding action created`, prefix);
96
- logger.debug(cb.toString(), prefix);
97
- // Push the binding to the list of bindings for this capability as a new BindingAction
98
- // with the callback function to preserve
99
- this._bindings.push({
100
- ...binding,
101
- callback: cb,
102
- });
103
-
104
- // Now only allow adding actions to the same binding
105
- return { Then };
106
- };
107
-
108
- const ThenSet = (merge: DeepPartial<InstanceType<T>>): BindToAction<T> => {
109
- // Add the new action to the binding
110
- Then(req => req.Merge(merge));
111
-
112
- return { Then };
113
- };
114
-
115
- function InNamespace(...namespaces: string[]): BindingWithName<T> {
116
- logger.debug(`Add namespaces filter ${namespaces}`, prefix);
117
- binding.filters.namespaces.push(...namespaces);
118
- return { WithLabel, WithAnnotation, WithName, Then, ThenSet };
119
- }
120
-
121
- function WithName(name: string): BindingFilter<T> {
122
- logger.debug(`Add name filter ${name}`, prefix);
123
- binding.filters.name = name;
124
- return { WithLabel, WithAnnotation, Then, ThenSet };
125
- }
126
-
127
- function WithLabel(key: string, value = ""): BindingFilter<T> {
128
- logger.debug(`Add label filter ${key}=${value}`, prefix);
129
- binding.filters.labels[key] = value;
130
- return { WithLabel, WithAnnotation, Then, ThenSet };
131
- }
132
-
133
- const WithAnnotation = (key: string, value = ""): BindingFilter<T> => {
134
- logger.debug(`Add annotation filter ${key}=${value}`, prefix);
135
- binding.filters.annotations[key] = value;
136
- return { WithLabel, WithAnnotation, Then, ThenSet };
137
- };
138
-
139
- const bindEvent = (event: Event) => {
140
- binding.event = event;
141
- return {
142
- InNamespace,
143
- Then,
144
- ThenSet,
145
- WithAnnotation,
146
- WithLabel,
147
- WithName,
148
- };
149
- };
150
-
151
- return {
152
- IsCreatedOrUpdated: () => bindEvent(Event.CreateOrUpdate),
153
- IsCreated: () => bindEvent(Event.Create),
154
- IsUpdated: () => bindEvent(Event.Update),
155
- IsDeleted: () => bindEvent(Event.Delete),
156
- };
157
- };
158
- }
package/src/lib/filter.ts DELETED
@@ -1,55 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
-
4
- import { Request } from "./k8s";
5
- import logger from "./logger";
6
- import { Binding } from "./types";
7
-
8
- /**
9
- * shouldSkipRequest determines if a request should be skipped based on the binding filters.
10
- *
11
- * @param binding the capability action binding
12
- * @param req the incoming request
13
- * @returns
14
- */
15
- export function shouldSkipRequest(binding: Binding, req: Request) {
16
- const { group, kind, version } = binding.kind;
17
- const { namespaces, labels, annotations } = binding.filters;
18
- const { metadata } = req.object;
19
-
20
- if (kind !== req.kind.kind) {
21
- logger.debug(`${req.kind.kind} does not match ${kind}`);
22
- return true;
23
- }
24
-
25
- if (group && group !== req.kind.group) {
26
- logger.debug(`${req.kind.group} does not match ${group}`);
27
- return true;
28
- }
29
-
30
- if (version && version !== req.kind.version) {
31
- logger.debug(`${req.kind.version} does not match ${version}`);
32
- return true;
33
- }
34
-
35
- if (namespaces.length && !namespaces.includes(req.namespace || "")) {
36
- logger.debug(`${req.namespace} is not in ${namespaces}`);
37
- return true;
38
- }
39
-
40
- for (const [key, value] of Object.entries(labels)) {
41
- if (metadata?.labels?.[key] !== value) {
42
- logger.debug(`${metadata?.labels?.[key]} does not match ${value}`);
43
- return true;
44
- }
45
- }
46
-
47
- for (const [key, value] of Object.entries(annotations)) {
48
- if (metadata?.annotations?.[key] !== value) {
49
- logger.debug(`${metadata?.annotations?.[key]} does not match ${value}`);
50
- return true;
51
- }
52
- }
53
-
54
- return false;
55
- }
@@ -1,10 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
-
4
- // Export kinds as a single object
5
- import * as kind from "./upstream";
6
- export { kind as a };
7
-
8
- export { modelToGroupVersionKind, gvkMap } from "./kinds";
9
-
10
- export * from "./types";