@highstate/library 0.9.4 → 0.9.6

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.
@@ -0,0 +1,55 @@
1
+ import { defineUnit, Type } from "@highstate/contract"
2
+ import { l3EndpointEntity, l4EndpointEntity } from "../network"
3
+ import { createSource } from "./shared"
4
+
5
+ export const explicitEndpointFilterSchema = Type.StringEnum(["public", "external", "internal"])
6
+
7
+ export const endpointFilter = defineUnit({
8
+ type: "apps.endpoint-filter",
9
+
10
+ args: {
11
+ /**
12
+ * The filter to apply to the endpoints.
13
+ *
14
+ * - `public`: Only public endpoints accessible from the internet.
15
+ * - `external`: Only external endpoints (e.g. NodePort, LoadBalancer) accessible from outside the cluster, but not from the internet.
16
+ * - `internal`: Only internal endpoints (e.g. ClusterIP) accessible from within the cluster.
17
+ */
18
+ filter: Type.Default(explicitEndpointFilterSchema, "public"),
19
+ },
20
+
21
+ inputs: {
22
+ l3Endpoints: {
23
+ entity: l3EndpointEntity,
24
+ multiple: true,
25
+ required: false,
26
+ },
27
+ l4Endpoints: {
28
+ entity: l4EndpointEntity,
29
+ multiple: true,
30
+ required: false,
31
+ },
32
+ },
33
+
34
+ outputs: {
35
+ l3Endpoints: {
36
+ entity: l3EndpointEntity,
37
+ multiple: true,
38
+ },
39
+ l4Endpoints: {
40
+ entity: l4EndpointEntity,
41
+ multiple: true,
42
+ },
43
+ },
44
+
45
+ meta: {
46
+ displayName: "Endpoint Filter",
47
+ description: "Explicitly filter endpoints by their accessibility.",
48
+ primaryIcon: "mdi:network-outline",
49
+ primaryIconColor: "#FF9800",
50
+ secondaryIcon: "mdi:filter-outline",
51
+ category: "Network",
52
+ },
53
+
54
+ source: createSource("endpoint-filter"),
55
+ })
@@ -1,18 +1,17 @@
1
1
  import { defineEntity, defineUnit, Type, type Static } from "@highstate/contract"
2
- import { clusterEntity, serviceEntity } from "../k8s"
3
- import { repoEntity } from "../restic"
4
- import { providerEntity } from "../dns"
2
+ import { serviceEntity } from "../k8s"
3
+ import {
4
+ createArgs,
5
+ createInputs,
6
+ createSource,
7
+ databaseSchema,
8
+ extraInputDefinitions,
9
+ } from "./shared"
5
10
 
6
11
  export const postgresqlEntity = defineEntity({
7
12
  type: "apps.postgresql",
8
13
 
9
- schema: Type.Object({
10
- service: Type.Optional(serviceEntity.schema),
11
-
12
- host: Type.String(),
13
- port: Type.Number(),
14
- rootPassword: Type.String(),
15
- }),
14
+ schema: databaseSchema,
16
15
 
17
16
  meta: {
18
17
  color: "#336791",
@@ -22,26 +21,13 @@ export const postgresqlEntity = defineEntity({
22
21
  export const postgresql = defineUnit({
23
22
  type: "apps.postgresql",
24
23
 
25
- args: {
26
- fqdn: Type.Optional(Type.String()),
27
- appName: Type.Optional(Type.String()),
28
- },
24
+ args: createArgs("postgresql", ["external"]),
29
25
 
30
26
  secrets: {
31
27
  rootPassword: Type.Optional(Type.String()),
32
28
  },
33
29
 
34
- inputs: {
35
- k8sCluster: clusterEntity,
36
- resticRepo: {
37
- entity: repoEntity,
38
- required: false,
39
- },
40
- dnsProvider: {
41
- entity: providerEntity,
42
- required: false,
43
- },
44
- },
30
+ inputs: createInputs(["resticRepo", "dnsProviders"]),
45
31
 
46
32
  outputs: {
47
33
  postgresql: postgresqlEntity,
@@ -53,14 +39,17 @@ export const postgresql = defineUnit({
53
39
  description: "The PostgreSQL database deployed on Kubernetes.",
54
40
  primaryIcon: "simple-icons:postgresql",
55
41
  secondaryIcon: "mdi:database",
42
+ category: "Databases",
56
43
  },
57
44
 
58
- source: {
59
- package: "@highstate/apps",
60
- path: "postgresql/app",
61
- },
45
+ source: createSource("postgresql/app"),
62
46
  })
63
47
 
48
+ extraInputDefinitions.postgresql = {
49
+ entity: postgresqlEntity,
50
+ displayName: "PostgreSQL",
51
+ }
52
+
64
53
  export const postgresqlDatabase = defineUnit({
65
54
  type: "apps.postgresql.database",
66
55
 
@@ -73,10 +62,7 @@ export const postgresqlDatabase = defineUnit({
73
62
  password: Type.Optional(Type.String()),
74
63
  },
75
64
 
76
- inputs: {
77
- k8sCluster: clusterEntity,
78
- postgresql: postgresqlEntity,
79
- },
65
+ inputs: createInputs(["postgresql"]),
80
66
 
81
67
  meta: {
82
68
  displayName: "PostgreSQL Database",
@@ -84,12 +70,10 @@ export const postgresqlDatabase = defineUnit({
84
70
  "The virtual PostgreSQL database created on the PostgreSQL instance. Works only for PostgreSQL instances deployed on Kubernetes.",
85
71
  primaryIcon: "simple-icons:postgresql",
86
72
  secondaryIcon: "mdi:database-plus",
73
+ category: "Databases",
87
74
  },
88
75
 
89
- source: {
90
- package: "@highstate/apps",
91
- path: "postgresql/database",
92
- },
76
+ source: createSource("postgresql/database"),
93
77
  })
94
78
 
95
79
  export type PostgreSQL = Static<typeof postgresqlEntity.schema>
@@ -0,0 +1,217 @@
1
+ import type { TString } from "@sinclair/typebox"
2
+ import type { mariadbEntity } from "./mariadb"
3
+ import type { postgresqlEntity } from "./postgresql"
4
+ import type { mongodbEntity } from "./mongodb"
5
+ import { Type } from "@highstate/contract"
6
+ import {
7
+ accessPointEntity,
8
+ clusterEntity,
9
+ persistentVolumeClaimEntity,
10
+ serviceEntity,
11
+ } from "../k8s"
12
+ import { repoEntity } from "../restic"
13
+ import { providerEntity } from "../dns"
14
+ import { l4EndpointEntity } from "../network"
15
+
16
+ const extraArgsDefinitions = {
17
+ fqdn: {
18
+ schema: Type.String(),
19
+ },
20
+ endpoints: {
21
+ schema: Type.Array(Type.String()),
22
+ required: false,
23
+ },
24
+ external: {
25
+ schema: Type.Boolean(),
26
+ required: false,
27
+ },
28
+ } as const
29
+
30
+ type LazyExtraInputDefinitions = {
31
+ mariadb: {
32
+ entity: typeof mariadbEntity
33
+ displayName: "MariaDB"
34
+ }
35
+ postgresql: {
36
+ entity: typeof postgresqlEntity
37
+ displayName: "PostgreSQL"
38
+ }
39
+ mongodb: {
40
+ entity: typeof mongodbEntity
41
+ displayName: "MongoDB"
42
+ }
43
+ }
44
+
45
+ const eagerExtraInputDefinitions = {
46
+ accessPoint: {
47
+ entity: accessPointEntity,
48
+ },
49
+ resticRepo: {
50
+ entity: repoEntity,
51
+ required: false,
52
+ },
53
+ dnsProviders: {
54
+ entity: providerEntity,
55
+ required: false,
56
+ multiple: true,
57
+ },
58
+ volume: {
59
+ entity: persistentVolumeClaimEntity,
60
+ required: false,
61
+ },
62
+ } as const
63
+
64
+ export const extraInputDefinitions = {
65
+ ...eagerExtraInputDefinitions,
66
+ } as typeof eagerExtraInputDefinitions & LazyExtraInputDefinitions
67
+
68
+ type ExtraArgsDefinitions = typeof extraArgsDefinitions
69
+ type ExtraArgsName = keyof ExtraArgsDefinitions
70
+
71
+ type CreateArgsOptions<R extends readonly ExtraArgsName[], O extends readonly ExtraArgsName[]> = {
72
+ required?: R
73
+ optional?: O
74
+ }
75
+
76
+ export function createArgs<T extends readonly ExtraArgsName[] = []>(
77
+ defaultAppName: string,
78
+ extraArgs?: T,
79
+ ): {
80
+ appName: TString & { default: string }
81
+ } & {
82
+ [K in T[number]]: ExtraArgsDefinitions[K]
83
+ }
84
+
85
+ export function createArgs<
86
+ R extends readonly ExtraArgsName[] = [],
87
+ O extends readonly ExtraArgsName[] = [],
88
+ >(
89
+ defaultAppName: string,
90
+ extraArgs?: CreateArgsOptions<R, O>,
91
+ ): {
92
+ appName: TString & { default: string }
93
+ } & {
94
+ [K in R[number]]: ExtraArgsDefinitions[K] & { required: true }
95
+ } & {
96
+ [K in O[number]]: ExtraArgsDefinitions[K] & { required: false }
97
+ }
98
+
99
+ export function createArgs<
100
+ R extends readonly ExtraArgsName[] = [],
101
+ O extends readonly ExtraArgsName[] = [],
102
+ >(defaultAppName: string, extraArgs?: readonly ExtraArgsName[] | CreateArgsOptions<R, O>) {
103
+ const base = {
104
+ appName: Type.Default(Type.String(), defaultAppName),
105
+ }
106
+
107
+ const dynamicArgs: Partial<Record<ExtraArgsName, object>> = {}
108
+
109
+ if (Array.isArray(extraArgs)) {
110
+ for (const name of extraArgs as readonly ExtraArgsName[]) {
111
+ dynamicArgs[name] = extraArgsDefinitions[name]
112
+ }
113
+ } else {
114
+ const { required, optional } = (extraArgs as CreateArgsOptions<R, O>) ?? {}
115
+
116
+ for (const name of required ?? []) {
117
+ dynamicArgs[name] = {
118
+ ...extraArgsDefinitions[name],
119
+ required: true,
120
+ }
121
+ }
122
+
123
+ for (const name of optional ?? []) {
124
+ dynamicArgs[name] = {
125
+ ...extraArgsDefinitions[name],
126
+ required: false,
127
+ }
128
+ }
129
+ }
130
+
131
+ return {
132
+ ...base,
133
+ ...dynamicArgs,
134
+ }
135
+ }
136
+
137
+ type ExtraInputDefinitions = typeof extraInputDefinitions
138
+ type ExtraInputName = keyof ExtraInputDefinitions
139
+
140
+ type CreateInputsOptions<
141
+ R extends readonly ExtraInputName[],
142
+ O extends readonly ExtraInputName[],
143
+ > = {
144
+ required?: R
145
+ optional?: O
146
+ }
147
+
148
+ export function createInputs<T extends readonly ExtraInputName[] = []>(
149
+ inputs?: T,
150
+ ): {
151
+ k8sCluster: typeof clusterEntity
152
+ } & {
153
+ [K in T[number]]: ExtraInputDefinitions[K]
154
+ }
155
+
156
+ export function createInputs<
157
+ R extends readonly ExtraInputName[] = [],
158
+ O extends readonly ExtraInputName[] = [],
159
+ >(
160
+ inputs?: CreateInputsOptions<R, O>,
161
+ ): {
162
+ k8sCluster: typeof clusterEntity
163
+ } & {
164
+ [K in R[number]]: ExtraInputDefinitions[K] & { required: true }
165
+ } & {
166
+ [K in O[number]]: ExtraInputDefinitions[K] & { required: false }
167
+ }
168
+
169
+ export function createInputs<
170
+ R extends readonly ExtraInputName[] = [],
171
+ O extends readonly ExtraInputName[] = [],
172
+ >(inputs?: readonly ExtraInputName[] | CreateInputsOptions<R, O>) {
173
+ const base = {
174
+ k8sCluster: clusterEntity,
175
+ }
176
+
177
+ const dynamicInputs: Partial<Record<ExtraInputName, object>> = {}
178
+
179
+ if (Array.isArray(inputs)) {
180
+ for (const name of inputs as readonly ExtraInputName[]) {
181
+ dynamicInputs[name] = extraInputDefinitions[name]
182
+ }
183
+ } else {
184
+ const { required, optional } = (inputs as CreateInputsOptions<R, O>) ?? {}
185
+
186
+ for (const name of required ?? []) {
187
+ dynamicInputs[name] = {
188
+ ...extraInputDefinitions[name],
189
+ required: true,
190
+ }
191
+ }
192
+ for (const name of optional ?? []) {
193
+ dynamicInputs[name] = {
194
+ ...extraInputDefinitions[name],
195
+ required: false,
196
+ }
197
+ }
198
+ }
199
+
200
+ return {
201
+ ...base,
202
+ ...dynamicInputs,
203
+ }
204
+ }
205
+
206
+ export function createSource(path: string) {
207
+ return {
208
+ package: "@highstate/apps",
209
+ path,
210
+ }
211
+ }
212
+
213
+ export const databaseSchema = Type.Object({
214
+ endpoints: Type.Array(l4EndpointEntity.schema),
215
+ service: Type.Optional(serviceEntity.schema),
216
+ rootPassword: Type.String(),
217
+ })
@@ -1,37 +1,37 @@
1
1
  import { defineUnit, Type } from "@highstate/contract"
2
- import {
3
- accessPointEntity,
4
- clusterEntity,
5
- persistentVolumeClaimEntity,
6
- serviceEntity,
7
- } from "../k8s"
8
- import { repoEntity } from "../restic"
2
+ import { persistentVolumeClaimEntity, serviceEntity } from "../k8s"
3
+ import { createArgs, createInputs, createSource } from "./shared"
9
4
 
10
- export const backupModeSchema = Type.Union([Type.Literal("metadata"), Type.Literal("full")])
5
+ export const backupModeSchema = Type.StringEnum(["state", "full"])
11
6
 
12
7
  export const syncthing = defineUnit({
13
8
  type: "apps.syncthing",
14
9
 
15
10
  args: {
16
- fqdn: Type.String(),
11
+ ...createArgs("syncthing", ["fqdn"]),
12
+
13
+ /**
14
+ * The FQDN of the Syncthing instance used to sync with other devices.
15
+ *
16
+ * The `fqdn` argument unlike this one points to the gateway and used to
17
+ * access the Syncthing Web UI.
18
+ */
17
19
  deviceFqdn: Type.Optional(Type.String()),
18
- appName: Type.Optional(Type.String()),
19
- backupMode: Type.Optional({ ...backupModeSchema, default: "metadata" }),
20
- },
21
20
 
22
- inputs: {
23
- accessPoint: accessPointEntity,
24
- k8sCluster: clusterEntity,
25
- resticRepo: {
26
- entity: repoEntity,
27
- required: false,
28
- },
29
- volume: {
30
- entity: persistentVolumeClaimEntity,
31
- required: false,
32
- },
21
+ /**
22
+ * The backup mode to use for the Syncthing instance.
23
+ *
24
+ * - `state`: Only the state is backed up. When the instance is restored, it will
25
+ * sync files from the other devices automatically.
26
+ * - `full`: A full backup is created including all files.
27
+ *
28
+ * The default is `state`.
29
+ */
30
+ backupMode: Type.Default(backupModeSchema, "state"),
33
31
  },
34
32
 
33
+ inputs: createInputs(["accessPoint", "resticRepo", "dnsProviders", "volume"]),
34
+
35
35
  outputs: {
36
36
  service: serviceEntity,
37
37
  volume: persistentVolumeClaimEntity,
@@ -41,10 +41,8 @@ export const syncthing = defineUnit({
41
41
  displayName: "Syncthing",
42
42
  description: "The Syncthing instance deployed on Kubernetes.",
43
43
  primaryIcon: "simple-icons:syncthing",
44
+ category: "File Sync",
44
45
  },
45
46
 
46
- source: {
47
- package: "@highstate/apps",
48
- path: "syncthing",
49
- },
47
+ source: createSource("syncthing"),
50
48
  })
@@ -0,0 +1,19 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import * as apps from "./mariadb"
3
+
4
+ import type { InstanceInput } from "@highstate/contract"
5
+
6
+ declare const cluster: InstanceInput<"k8s.cluster">
7
+
8
+ const { mariadb, service } = apps.mariadb({
9
+ name: "test",
10
+ args: {
11
+ external: true,
12
+ },
13
+ inputs: {
14
+ k8sCluster: cluster,
15
+ },
16
+ })
17
+
18
+ void mariadb
19
+ void service
@@ -1,18 +1,16 @@
1
1
  import { defineUnit, Type } from "@highstate/contract"
2
- import { clusterEntity, gatewayEntity, serviceEntity, serviceTypeSchema } from "../k8s"
2
+ import { gatewayEntity, serviceEntity } from "../k8s"
3
+ import { createArgs, createInputs } from "./shared"
3
4
 
4
5
  export const traefikGateway = defineUnit({
5
6
  type: "apps.traefik-gateway",
6
7
 
7
8
  args: {
8
- appName: Type.Optional(Type.String()),
9
+ ...createArgs("traefik", ["external"]),
9
10
  className: Type.Optional(Type.String()),
10
- serviceType: Type.Optional(serviceTypeSchema),
11
11
  },
12
12
 
13
- inputs: {
14
- k8sCluster: clusterEntity,
15
- },
13
+ inputs: createInputs(),
16
14
 
17
15
  outputs: {
18
16
  gateway: gatewayEntity,
@@ -23,6 +21,7 @@ export const traefikGateway = defineUnit({
23
21
  displayName: "Traefik Gateway",
24
22
  description: "A Traefik gateway for routing traffic to services.",
25
23
  primaryIcon: "simple-icons:traefikproxy",
24
+ category: "Network",
26
25
  },
27
26
 
28
27
  source: {
@@ -1,33 +1,23 @@
1
1
  import { defineUnit, Type } from "@highstate/contract"
2
- import { accessPointEntity, clusterEntity } from "../k8s"
3
- import { mariadbEntity } from "./mariadb"
2
+ import { createArgs, createInputs, createSource } from "./shared"
4
3
 
5
4
  export const vaultwarden = defineUnit({
6
5
  type: "apps.vaultwarden",
7
6
 
8
- args: {
9
- fqdn: Type.String(),
10
- appName: Type.Optional(Type.String()),
11
- },
12
-
13
- inputs: {
14
- mariadb: mariadbEntity,
15
- accessPoint: accessPointEntity,
16
- k8sCluster: clusterEntity,
17
- },
7
+ args: createArgs("vaultwarden", ["fqdn"]),
18
8
 
19
9
  secrets: {
20
10
  mariadbPassword: Type.Optional(Type.String()),
21
11
  },
22
12
 
13
+ inputs: createInputs(["accessPoint", "mariadb"]),
14
+
23
15
  meta: {
24
16
  displayName: "Vaultwarden",
25
17
  description: "The Vaultwarden password manager deployed on Kubernetes.",
26
18
  primaryIcon: "simple-icons:vaultwarden",
19
+ category: "Security",
27
20
  },
28
21
 
29
- source: {
30
- package: "@highstate/apps",
31
- path: "vaultwarden",
32
- },
22
+ source: createSource("vaultwarden"),
33
23
  })
@@ -1,22 +1,12 @@
1
- import { defineUnit, Type } from "@highstate/contract"
2
- import { accessPointEntity, clusterEntity } from "../k8s"
3
- import { postgresqlEntity } from "./postgresql"
1
+ import { defineUnit } from "@highstate/contract"
2
+ import { createArgs, createInputs } from "./shared"
4
3
 
5
4
  export const zitadel = defineUnit({
6
5
  type: "apps.zitadel",
7
6
 
8
- args: {
9
- domain: Type.String(),
10
- },
7
+ args: createArgs("zitadel", ["fqdn"]),
11
8
 
12
- inputs: {
13
- postgresql: {
14
- entity: postgresqlEntity,
15
- displayName: "PostgreSQL",
16
- },
17
- accessPoint: accessPointEntity,
18
- k8sCluster: clusterEntity,
19
- },
9
+ inputs: createInputs(["accessPoint", "postgresql"]),
20
10
 
21
11
  meta: {
22
12
  displayName: "Zitadel",
package/src/cloudflare.ts CHANGED
@@ -16,6 +16,7 @@ export const connection = defineUnit({
16
16
  displayName: "Cloudflare Connection",
17
17
  description: "Creates a new Cloudflare connection for one zone.",
18
18
  primaryIcon: "simple-icons:cloudflare",
19
+ category: "Cloudflare",
19
20
  },
20
21
 
21
22
  source: {