mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
@@ -21,23 +21,78 @@ type FilterKinds = "onion" | "local" | "domain" | "ip" | "ipv4" | "ipv6"
|
||||
export type Filter = {
|
||||
visibility?: "public" | "private"
|
||||
kind?: FilterKinds | FilterKinds[]
|
||||
predicate?: (h: HostnameInfo) => boolean
|
||||
exclude?: Filter
|
||||
}
|
||||
|
||||
type VisibilityFilter<V extends "public" | "private"> = V extends "public"
|
||||
? (HostnameInfo & { public: true }) | VisibilityFilter<Exclude<V, "public">>
|
||||
: V extends "private"
|
||||
?
|
||||
| (HostnameInfo & { public: false })
|
||||
| VisibilityFilter<Exclude<V, "private">>
|
||||
: never
|
||||
type KindFilter<K extends FilterKinds> = K extends "onion"
|
||||
? (HostnameInfo & { kind: "onion" }) | KindFilter<Exclude<K, "onion">>
|
||||
: K extends "local"
|
||||
?
|
||||
| (HostnameInfo & { kind: "ip"; hostname: { kind: "local" } })
|
||||
| KindFilter<Exclude<K, "local">>
|
||||
: K extends "domain"
|
||||
?
|
||||
| (HostnameInfo & { kind: "ip"; hostname: { kind: "domain" } })
|
||||
| KindFilter<Exclude<K, "domain">>
|
||||
: K extends "ipv4"
|
||||
?
|
||||
| (HostnameInfo & { kind: "ip"; hostname: { kind: "ipv4" } })
|
||||
| KindFilter<Exclude<K, "ipv4">>
|
||||
: K extends "ipv6"
|
||||
?
|
||||
| (HostnameInfo & { kind: "ip"; hostname: { kind: "ipv6" } })
|
||||
| KindFilter<Exclude<K, "ipv6">>
|
||||
: K extends "ip"
|
||||
? KindFilter<Exclude<K, "ip"> | "ipv4" | "ipv6">
|
||||
: never
|
||||
|
||||
type FilterReturnTy<F extends Filter> = F extends {
|
||||
visibility: infer V extends "public" | "private"
|
||||
}
|
||||
? VisibilityFilter<V> & FilterReturnTy<Omit<F, "visibility">>
|
||||
: F extends {
|
||||
kind: (infer K extends FilterKinds) | (infer K extends FilterKinds)[]
|
||||
}
|
||||
? KindFilter<K> & FilterReturnTy<Omit<F, "kind">>
|
||||
: F extends {
|
||||
predicate: (h: HostnameInfo) => h is infer H extends HostnameInfo
|
||||
}
|
||||
? H & FilterReturnTy<Omit<F, "predicate">>
|
||||
: F extends { exclude: infer E extends Filter } // MUST BE LAST
|
||||
? HostnameInfo extends FilterReturnTy<E>
|
||||
? HostnameInfo
|
||||
: Exclude<HostnameInfo, FilterReturnTy<E>>
|
||||
: HostnameInfo
|
||||
|
||||
type Formats = "hostname-info" | "urlstring" | "url"
|
||||
type FormatReturnTy<Format extends Formats> = Format extends "hostname-info"
|
||||
? HostnameInfo
|
||||
type FormatReturnTy<
|
||||
F extends Filter,
|
||||
Format extends Formats,
|
||||
> = Format extends "hostname-info"
|
||||
? FilterReturnTy<F> | FormatReturnTy<F, Exclude<Format, "hostname-info">>
|
||||
: Format extends "url"
|
||||
? URL
|
||||
: UrlString
|
||||
? URL | FormatReturnTy<F, Exclude<Format, "url">>
|
||||
: Format extends "urlstring"
|
||||
? UrlString | FormatReturnTy<F, Exclude<Format, "urlstring">>
|
||||
: never
|
||||
|
||||
export type Filled = {
|
||||
hostnames: HostnameInfo[]
|
||||
|
||||
filter: <Format extends Formats = "urlstring">(
|
||||
filter: Filter,
|
||||
toUrl: (h: HostnameInfo) => UrlString[]
|
||||
|
||||
filter: <F extends Filter, Format extends Formats = "urlstring">(
|
||||
filter: F,
|
||||
format?: Format,
|
||||
) => FormatReturnTy<Format>[]
|
||||
) => FormatReturnTy<F, Format>[]
|
||||
|
||||
publicHostnames: HostnameInfo[]
|
||||
onionHostnames: HostnameInfo[]
|
||||
@@ -83,7 +138,7 @@ const negate =
|
||||
const unique = <A>(values: A[]) => Array.from(new Set(values))
|
||||
export const addressHostToUrl = (
|
||||
{ scheme, sslScheme, username, suffix }: AddressInfo,
|
||||
host: HostnameInfo,
|
||||
hostname: HostnameInfo,
|
||||
): UrlString[] => {
|
||||
const res = []
|
||||
const fmt = (scheme: string | null, host: HostnameInfo, port: number) => {
|
||||
@@ -109,11 +164,11 @@ export const addressHostToUrl = (
|
||||
username ? `${username}@` : ""
|
||||
}${hostname}${excludePort ? "" : `:${port}`}${suffix}`
|
||||
}
|
||||
if (host.hostname.sslPort !== null) {
|
||||
res.push(fmt(sslScheme, host, host.hostname.sslPort))
|
||||
if (hostname.hostname.sslPort !== null) {
|
||||
res.push(fmt(sslScheme, hostname, hostname.hostname.sslPort))
|
||||
}
|
||||
if (host.hostname.port !== null) {
|
||||
res.push(fmt(scheme, host, host.hostname.port))
|
||||
if (hostname.hostname.port !== null) {
|
||||
res.push(fmt(scheme, hostname, hostname.hostname.port))
|
||||
}
|
||||
|
||||
return res
|
||||
@@ -124,6 +179,10 @@ function filterRec(
|
||||
filter: Filter,
|
||||
invert: boolean,
|
||||
): HostnameInfo[] {
|
||||
if (filter.predicate) {
|
||||
const pred = filter.predicate
|
||||
hostnames = hostnames.filter((h) => invert !== pred(h))
|
||||
}
|
||||
if (filter.visibility === "public")
|
||||
hostnames = hostnames.filter(
|
||||
(h) => invert !== (h.kind === "onion" || h.public),
|
||||
@@ -170,13 +229,18 @@ export const filledAddress = (
|
||||
return {
|
||||
...addressInfo,
|
||||
hostnames,
|
||||
filter: <T extends Formats = "urlstring">(filter: Filter, format?: T) => {
|
||||
const res = filterRec(hostnames, filter, false)
|
||||
if (format === "hostname-info") return res as FormatReturnTy<T>[]
|
||||
const urls = res.flatMap(toUrl)
|
||||
if (format === "url")
|
||||
return urls.map((u) => new URL(u)) as FormatReturnTy<T>[]
|
||||
return urls as FormatReturnTy<T>[]
|
||||
toUrl,
|
||||
filter: <F extends Filter, Format extends Formats = "urlstring">(
|
||||
filter: F,
|
||||
format?: Format,
|
||||
) => {
|
||||
const filtered = filterRec(hostnames, filter, false)
|
||||
let res: FormatReturnTy<F, Format>[] = filtered as any
|
||||
if (format === "hostname-info") return res
|
||||
const urls = filtered.flatMap(toUrl)
|
||||
if (format === "url") res = urls.map((u) => new URL(u)) as any
|
||||
else res = urls as any
|
||||
return res
|
||||
},
|
||||
get publicHostnames() {
|
||||
return hostnames.filter((h) => h.kind === "onion" || h.public)
|
||||
|
||||
@@ -11,7 +11,7 @@ import * as CP from "node:child_process"
|
||||
|
||||
export { Daemon } from "./Daemon"
|
||||
export { CommandController } from "./CommandController"
|
||||
import { HealthDaemon } from "./HealthDaemon"
|
||||
import { EXIT_SUCCESS, HealthDaemon } from "./HealthDaemon"
|
||||
import { Daemon } from "./Daemon"
|
||||
import { CommandController } from "./CommandController"
|
||||
import { HealthCheck } from "../health/HealthCheck"
|
||||
@@ -91,6 +91,10 @@ type NewDaemonParams<
|
||||
subcontainer: C
|
||||
}
|
||||
|
||||
type OptionalParamSync<T> = T | (() => T | null)
|
||||
type OptionalParamAsync<T> = () => Promise<T | null>
|
||||
type OptionalParam<T> = OptionalParamSync<T> | OptionalParamAsync<T>
|
||||
|
||||
type AddDaemonParams<
|
||||
Manifest extends T.SDKManifest,
|
||||
Ids extends string,
|
||||
@@ -192,6 +196,37 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
[],
|
||||
)
|
||||
}
|
||||
|
||||
private addDaemonImpl<Id extends string>(
|
||||
id: Id,
|
||||
daemon: Promise<
|
||||
Daemon<Manifest, SubContainer<Manifest, T.Effects> | null>
|
||||
> | null,
|
||||
requires: Ids[],
|
||||
ready: Ready | typeof EXIT_SUCCESS,
|
||||
) {
|
||||
const healthDaemon = new HealthDaemon(
|
||||
daemon,
|
||||
requires
|
||||
.map((x) => this.ids.indexOf(x))
|
||||
.filter((x) => x >= 0)
|
||||
.map((id) => this.healthDaemons[id]),
|
||||
id,
|
||||
ready,
|
||||
this.effects,
|
||||
)
|
||||
const daemons = daemon ? [...this.daemons, daemon] : [...this.daemons]
|
||||
const ids = [...this.ids, id] as (Ids | Id)[]
|
||||
const healthDaemons = [...this.healthDaemons, healthDaemon]
|
||||
return new Daemons<Manifest, Ids | Id>(
|
||||
this.effects,
|
||||
this.started,
|
||||
daemons,
|
||||
ids,
|
||||
healthDaemons,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete list of daemons, including the one defined here
|
||||
* @param id
|
||||
@@ -205,36 +240,42 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
ErrorDuplicateId<Id> extends Id ? never :
|
||||
Id extends Ids ? ErrorDuplicateId<Id> :
|
||||
Id,
|
||||
options: AddDaemonParams<Manifest, Ids, Id, C>,
|
||||
options: OptionalParamSync<AddDaemonParams<Manifest, Ids, Id, C>>,
|
||||
): Daemons<Manifest, Ids | Id>
|
||||
addDaemon<Id extends string, C extends SubContainer<Manifest> | null>(
|
||||
// prettier-ignore
|
||||
id:
|
||||
"" extends Id ? never :
|
||||
ErrorDuplicateId<Id> extends Id ? never :
|
||||
Id extends Ids ? ErrorDuplicateId<Id> :
|
||||
Id,
|
||||
options: OptionalParamAsync<AddDaemonParams<Manifest, Ids, Id, C>>,
|
||||
): Promise<Daemons<Manifest, Ids | Id>>
|
||||
addDaemon<Id extends string, C extends SubContainer<Manifest> | null>(
|
||||
id: Id,
|
||||
options: OptionalParam<AddDaemonParams<Manifest, Ids, Id, C>>,
|
||||
) {
|
||||
const daemon =
|
||||
"daemon" in options
|
||||
? Promise.resolve(options.daemon)
|
||||
: Daemon.of<Manifest>()<C>(
|
||||
this.effects,
|
||||
options.subcontainer,
|
||||
options.exec,
|
||||
)
|
||||
const healthDaemon = new HealthDaemon(
|
||||
daemon,
|
||||
options.requires
|
||||
.map((x) => this.ids.indexOf(x))
|
||||
.filter((x) => x >= 0)
|
||||
.map((id) => this.healthDaemons[id]),
|
||||
id,
|
||||
options.ready,
|
||||
this.effects,
|
||||
)
|
||||
const daemons = [...this.daemons, daemon]
|
||||
const ids = [...this.ids, id] as (Ids | Id)[]
|
||||
const healthDaemons = [...this.healthDaemons, healthDaemon]
|
||||
return new Daemons<Manifest, Ids | Id>(
|
||||
this.effects,
|
||||
this.started,
|
||||
daemons,
|
||||
ids,
|
||||
healthDaemons,
|
||||
)
|
||||
const prev = this
|
||||
const res = (options: AddDaemonParams<Manifest, Ids, Id, C> | null) => {
|
||||
if (!options) return prev
|
||||
const daemon =
|
||||
"daemon" in options
|
||||
? Promise.resolve(options.daemon)
|
||||
: Daemon.of<Manifest>()<C>(
|
||||
this.effects,
|
||||
options.subcontainer,
|
||||
options.exec,
|
||||
)
|
||||
return prev.addDaemonImpl(id, daemon, options.requires, options.ready)
|
||||
}
|
||||
if (options instanceof Function) {
|
||||
const opts = options()
|
||||
if (opts instanceof Promise) {
|
||||
return opts.then(res)
|
||||
}
|
||||
return res(opts)
|
||||
}
|
||||
return res(options)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,40 +286,45 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
* @returns a new Daemons object
|
||||
*/
|
||||
addOneshot<Id extends string, C extends SubContainer<Manifest> | null>(
|
||||
id: "" extends Id
|
||||
? never
|
||||
: ErrorDuplicateId<Id> extends Id
|
||||
? never
|
||||
: Id extends Ids
|
||||
? ErrorDuplicateId<Id>
|
||||
: Id,
|
||||
options: AddOneshotParams<Manifest, Ids, Id, C>,
|
||||
// prettier-ignore
|
||||
id:
|
||||
"" extends Id ? never :
|
||||
ErrorDuplicateId<Id> extends Id ? never :
|
||||
Id extends Ids ? ErrorDuplicateId<Id> :
|
||||
Id,
|
||||
options: OptionalParamSync<AddOneshotParams<Manifest, Ids, Id, C>>,
|
||||
): Daemons<Manifest, Ids | Id>
|
||||
addOneshot<Id extends string, C extends SubContainer<Manifest> | null>(
|
||||
// prettier-ignore
|
||||
id:
|
||||
"" extends Id ? never :
|
||||
ErrorDuplicateId<Id> extends Id ? never :
|
||||
Id extends Ids ? ErrorDuplicateId<Id> :
|
||||
Id,
|
||||
options: OptionalParamAsync<AddOneshotParams<Manifest, Ids, Id, C>>,
|
||||
): Promise<Daemons<Manifest, Ids | Id>>
|
||||
addOneshot<Id extends string, C extends SubContainer<Manifest> | null>(
|
||||
id: Id,
|
||||
options: OptionalParam<AddOneshotParams<Manifest, Ids, Id, C>>,
|
||||
) {
|
||||
const daemon = Oneshot.of<Manifest>()<C>(
|
||||
this.effects,
|
||||
options.subcontainer,
|
||||
options.exec,
|
||||
)
|
||||
const healthDaemon = new HealthDaemon<Manifest>(
|
||||
daemon,
|
||||
options.requires
|
||||
.map((x) => this.ids.indexOf(x))
|
||||
.filter((x) => x >= 0)
|
||||
.map((id) => this.healthDaemons[id]),
|
||||
id,
|
||||
"EXIT_SUCCESS",
|
||||
this.effects,
|
||||
)
|
||||
const daemons = [...this.daemons, daemon]
|
||||
const ids = [...this.ids, id] as (Ids | Id)[]
|
||||
const healthDaemons = [...this.healthDaemons, healthDaemon]
|
||||
return new Daemons<Manifest, Ids | Id>(
|
||||
this.effects,
|
||||
this.started,
|
||||
daemons,
|
||||
ids,
|
||||
healthDaemons,
|
||||
)
|
||||
const prev = this
|
||||
const res = (options: AddOneshotParams<Manifest, Ids, Id, C> | null) => {
|
||||
if (!options) return prev
|
||||
const daemon = Oneshot.of<Manifest>()<C>(
|
||||
this.effects,
|
||||
options.subcontainer,
|
||||
options.exec,
|
||||
)
|
||||
return prev.addDaemonImpl(id, daemon, options.requires, EXIT_SUCCESS)
|
||||
}
|
||||
if (options instanceof Function) {
|
||||
const opts = options()
|
||||
if (opts instanceof Promise) {
|
||||
return opts.then(res)
|
||||
}
|
||||
return res(opts)
|
||||
}
|
||||
return res(options)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,35 +334,40 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
* @returns a new Daemons object
|
||||
*/
|
||||
addHealthCheck<Id extends string>(
|
||||
id: "" extends Id
|
||||
? never
|
||||
: ErrorDuplicateId<Id> extends Id
|
||||
? never
|
||||
: Id extends Ids
|
||||
? ErrorDuplicateId<Id>
|
||||
: Id,
|
||||
options: AddHealthCheckParams<Ids, Id>,
|
||||
// prettier-ignore
|
||||
id:
|
||||
"" extends Id ? never :
|
||||
ErrorDuplicateId<Id> extends Id ? never :
|
||||
Id extends Ids ? ErrorDuplicateId<Id> :
|
||||
Id,
|
||||
options: OptionalParamSync<AddHealthCheckParams<Ids, Id>>,
|
||||
): Daemons<Manifest, Ids | Id>
|
||||
addHealthCheck<Id extends string>(
|
||||
// prettier-ignore
|
||||
id:
|
||||
"" extends Id ? never :
|
||||
ErrorDuplicateId<Id> extends Id ? never :
|
||||
Id extends Ids ? ErrorDuplicateId<Id> :
|
||||
Id,
|
||||
options: OptionalParamAsync<AddHealthCheckParams<Ids, Id>>,
|
||||
): Promise<Daemons<Manifest, Ids | Id>>
|
||||
addHealthCheck<Id extends string>(
|
||||
id: Id,
|
||||
options: OptionalParam<AddHealthCheckParams<Ids, Id>>,
|
||||
) {
|
||||
const healthDaemon = new HealthDaemon<Manifest>(
|
||||
null,
|
||||
options.requires
|
||||
.map((x) => this.ids.indexOf(x))
|
||||
.filter((x) => x >= 0)
|
||||
.map((id) => this.healthDaemons[id]),
|
||||
id,
|
||||
options.ready,
|
||||
this.effects,
|
||||
)
|
||||
const daemons = [...this.daemons]
|
||||
const ids = [...this.ids, id] as (Ids | Id)[]
|
||||
const healthDaemons = [...this.healthDaemons, healthDaemon]
|
||||
return new Daemons<Manifest, Ids | Id>(
|
||||
this.effects,
|
||||
this.started,
|
||||
daemons,
|
||||
ids,
|
||||
healthDaemons,
|
||||
)
|
||||
const prev = this
|
||||
const res = (options: AddHealthCheckParams<Ids, Id> | null) => {
|
||||
if (!options) return prev
|
||||
return prev.addDaemonImpl(id, null, options.requires, EXIT_SUCCESS)
|
||||
}
|
||||
if (options instanceof Function) {
|
||||
const opts = options()
|
||||
if (opts instanceof Promise) {
|
||||
return opts.then(res)
|
||||
}
|
||||
return res(opts)
|
||||
}
|
||||
return res(options)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
4
sdk/package/package-lock.json
generated
4
sdk/package/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.4.0-beta.38",
|
||||
"version": "0.4.0-beta.40",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.4.0-beta.38",
|
||||
"version": "0.4.0-beta.40",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@iarna/toml": "^3.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.4.0-beta.38",
|
||||
"version": "0.4.0-beta.40",
|
||||
"description": "Software development kit to facilitate packaging services for StartOS",
|
||||
"main": "./package/lib/index.js",
|
||||
"types": "./package/lib/index.d.ts",
|
||||
|
||||
Reference in New Issue
Block a user