feat: Create the setupInterfaces types

This commit is contained in:
BluJ
2023-05-11 14:50:40 -06:00
parent 97701967b7
commit ed56e39f00
21 changed files with 142 additions and 148 deletions

View File

@@ -1,4 +0,0 @@
declare const AddressProof: unique symbol
export type AddressReceipt = {
[AddressProof]: never
}

View File

@@ -4,7 +4,6 @@ import { Trigger } from "../trigger"
import { TriggerInput } from "../trigger/TriggerInput"
import { defaultTrigger } from "../trigger/defaultTrigger"
import { DaemonReturned, Effects, ValidIfNoStupidEscape } from "../types"
import { InterfaceReceipt } from "./interfaceReceipt"
type Daemon<Ids extends string, Command extends string, Id extends string> = {
id: "" extends Id ? never : Id
command: ValidIfNoStupidEscape<Command> | [string, ...string[]]

View File

@@ -1,223 +0,0 @@
import { object, string } from "ts-matches"
import { Effects } from "../types"
import { NetworkInterfaceBuilder } from "./NetworkInterfaceBuilder"
import { Origin } from "./Origin"
const knownProtocols = {
http: {
secure: false,
ssl: false,
defaultPort: 80,
withSsl: "https",
},
https: {
secure: true,
ssl: true,
defaultPort: 443,
},
ws: {
secure: false,
ssl: false,
defaultPort: 80,
withSsl: "wss",
},
wss: {
secure: true,
ssl: true,
defaultPort: 443,
},
ssh: {
secure: true,
ssl: false,
defaultPort: 22,
},
bitcoin: {
secure: true,
ssl: false,
defaultPort: 8333,
},
grpc: {
secure: true,
ssl: true,
defaultPort: 50051,
},
dns: {
secure: true,
ssl: false,
defaultPort: 53,
},
} as const
type Scheme = string | null
type AddSslOptions = {
preferredExternalPort: number
scheme: Scheme
addXForwardedHeaders?: boolean /** default: false */
}
type Security = { secure: false; ssl: false } | { secure: true; ssl: boolean }
export type PortOptions = {
scheme: Scheme
preferredExternalPort: number
addSsl: AddSslOptions | null
} & Security
type KnownProtocols = typeof knownProtocols
type ProtocolsWithSslVariants = {
[K in keyof KnownProtocols]: KnownProtocols[K] extends {
withSsl: string
}
? K
: never
}[keyof KnownProtocols]
type NotProtocolsWithSslVariants = Exclude<
keyof KnownProtocols,
ProtocolsWithSslVariants
>
type PortOptionsByKnownProtocol =
| ({
protocol: ProtocolsWithSslVariants
preferredExternalPort?: number
scheme?: Scheme
} & ({ noAddSsl: true } | { addSsl?: Partial<AddSslOptions> }))
| {
protocol: NotProtocolsWithSslVariants
preferredExternalPort?: number
scheme?: Scheme
addSsl?: AddSslOptions | null
}
type PortOptionsByProtocol = PortOptionsByKnownProtocol | PortOptions
const hasStringProtocal = object({
protocol: string,
}).test
export class Host {
constructor(
readonly kind: "static" | "single" | "multi",
readonly options: {
effects: Effects
id: string
},
) {}
async bindPort(
internalPort: number,
options: PortOptionsByProtocol,
): Promise<Origin<this>> {
if (hasStringProtocal(options)) {
return await this.bindPortForKnown(options, internalPort)
} else {
return await this.bindPortForUnknown(internalPort, options)
}
}
private async bindPortForUnknown(
internalPort: number,
options:
| ({
scheme: Scheme
preferredExternalPort: number
addSsl: AddSslOptions | null
} & { secure: false; ssl: false })
| ({
scheme: Scheme
preferredExternalPort: number
addSsl: AddSslOptions | null
} & { secure: true; ssl: boolean }),
) {
await this.options.effects.bind({
kind: this.kind,
id: this.options.id,
internalPort: internalPort,
...options,
})
return new Origin(this, options)
}
private async bindPortForKnown(
options: PortOptionsByKnownProtocol,
internalPort: number,
) {
const scheme =
options.scheme === undefined ? options.protocol : options.scheme
const protoInfo = knownProtocols[options.protocol]
const preferredExternalPort =
options.preferredExternalPort ||
knownProtocols[options.protocol].defaultPort
const addSsl = this.getAddSsl(options, protoInfo)
const security: Security = !protoInfo.secure
? {
secure: protoInfo.secure,
ssl: protoInfo.ssl,
}
: { secure: false, ssl: false }
const newOptions = {
scheme,
preferredExternalPort,
addSsl,
...security,
}
await this.options.effects.bind({
kind: this.kind,
id: this.options.id,
internalPort,
...newOptions,
})
return new Origin(this, newOptions)
}
private getAddSsl(
options: PortOptionsByKnownProtocol,
protoInfo: KnownProtocols[keyof KnownProtocols],
): AddSslOptions | null {
if ("noAddSsl" in options && options.noAddSsl) return null
if ("withSsl" in protoInfo && protoInfo.withSsl)
return {
preferredExternalPort: knownProtocols[protoInfo.withSsl].defaultPort,
scheme: protoInfo.withSsl,
...("addSsl" in options ? options.addSsl : null),
}
return null
}
}
export class StaticHost extends Host {
constructor(options: { effects: Effects; id: string }) {
super("static", options)
}
}
export class SingleHost extends Host {
constructor(options: { effects: Effects; id: string }) {
super("single", options)
}
}
export class MultiHost extends Host {
constructor(options: { effects: Effects; id: string }) {
super("multi", options)
}
}
async function test(effects: Effects) {
const foo = new MultiHost({ effects, id: "foo" })
const fooOrigin = await foo.bindPort(80, { protocol: "http" as const })
const fooInterface = new NetworkInterfaceBuilder({
effects,
name: "Foo",
id: "foo",
description: "A Foo",
ui: true,
username: "bar",
path: "/baz",
search: { qux: "yes" },
})
await fooInterface.export([fooOrigin])
}

View File

@@ -1,13 +0,0 @@
import { Effects } from "../types"
import { TorHostname } from "./TorHostname"
export class NetworkBuilder {
static of(effects: Effects) {
return new NetworkBuilder(effects)
}
private constructor(private effects: Effects) {}
getTorHostName(id: string) {
return new TorHostname(this.effects, id)
}
}

View File

@@ -1,56 +0,0 @@
import { Effects } from "../types"
import { AddressReceipt } from "./AddressReceipt"
import { Host } from "./Host"
import { Origin } from "./Origin"
/**
* A helper class for creating a Network Interface
*
* Network Interfaces are collections of web addresses that expose the same API or other resource,
* display to the user with under a common name and description.
*
* All URIs on an interface inherit the same ui: bool, basic auth credentials, path, and search (query) params
*
* @param options
* @returns
*/
export class NetworkInterfaceBuilder {
constructor(
readonly options: {
effects: Effects
name: string
id: string
description: string
ui: boolean
username: null | string
path: string
search: Record<string, string>
},
) {}
/**
* A function to register a group of origins (<PROTOCOL> :// <HOSTNAME> : <PORT>) with StartOS
*
* The returned addressReceipt serves as proof that the addresses were registered
*
* @param addresses
* @returns
*/
async export(origins: Iterable<Origin<Host>>) {
const { name, description, id, ui, username, path, search } = this.options
const addresses = Array.from(origins).map((o) =>
o.build({ username, path, search }),
)
await this.options.effects.exportNetworkInterface({
id,
name,
description,
addresses,
ui,
})
return {} as AddressReceipt
}
}

View File

@@ -1,29 +0,0 @@
import { Address } from "../types"
import { Host, PortOptions } from "./Host"
export class Origin<T extends Host> {
constructor(readonly host: T, readonly options: PortOptions) {}
build({ username, path, search }: BuildOptions): Address {
const qpEntries = Object.entries(search)
.map(
([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`,
)
.join("&")
const qp = qpEntries.length ? `?${qpEntries}` : ""
return {
hostId: this.host.options.id,
options: this.options,
suffix: `${path}${qp}`,
username,
}
}
}
type BuildOptions = {
username: string | null
path: string
search: Record<string, string>
}

View File

@@ -1,4 +0,0 @@
export declare const ReadyProof: unique symbol
export type ReadyReceipt = {
[ReadyProof]: never
}

View File

@@ -1,7 +0,0 @@
import { Daemon } from "../types"
import { ReadyProof } from "./ReadyProof"
export type RunningMainRet = {
[ReadyProof]: never
daemon: Daemon
}

View File

@@ -1,8 +0,0 @@
import { Origin } from "./Origin"
export class TorBinding {
constructor(readonly host: string) {}
createOrigin(protocol: string | null) {
return new Origin(protocol, this.host)
}
}

View File

@@ -1,17 +0,0 @@
import { Effects } from "../types"
import { TorBinding } from "./TorBinding"
export class TorHostname {
constructor(readonly effects: Effects, readonly id: string) {}
static of(effects: Effects, id: string) {
return new TorHostname(effects, id)
}
async bindTor(internalPort: number, externalPort: number) {
const address = await this.effects.bindTor({
internalPort,
name: this.id,
externalPort,
})
return new TorBinding(address)
}
}

View File

@@ -1,13 +0,0 @@
import { AddressReceipt } from "./AddressReceipt"
import { InterfaceReceipt } from "./interfaceReceipt"
/**
* Takes a list of addressReceipts
*
* Returns an interfaceReceipt, which is needed to create the service daemons
*/
export const exportInterfaces = (
_firstProof: AddressReceipt,
..._rest: AddressReceipt[]
) => ({} as InterfaceReceipt)
export default exportInterfaces

View File

@@ -2,14 +2,8 @@ import { Effects, ExpectedExports } from "../types"
import { createMainUtils } from "../util"
import { Utils, utils } from "../util/utils"
import { Daemons } from "./Daemons"
import "./exportInterfaces"
import "./LocalBinding"
import "./LocalPort"
import "./NetworkBuilder"
import "./NetworkInterfaceBuilder"
import "./Origin"
import "./TorBinding"
import "./TorHostname"
import "../interfaces/NetworkInterfaceBuilder"
import "../interfaces/Origin"
import "./Daemons"

View File

@@ -1,4 +0,0 @@
declare const InterfaceProof: unique symbol
export type InterfaceReceipt = {
[InterfaceProof]: never
}