mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
Refactor/sdk init (#2947)
* fixes for main * refactor package initialization * fixes from testing * more fixes * beta.21 * do not use instanceof * closes #2921 * beta22 * allow disabling kiosk * migration * fix /etc/shadow * actionRequest -> task * beta.23
This commit is contained in:
18
build/lib/scripts/forward-port
Normal file
18
build/lib/scripts/forward-port
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
iptables -F
|
||||
iptables -t nat -F
|
||||
|
||||
iptables -t nat -A POSTROUTING -o $iiface -j MASQUERADE
|
||||
iptables -t nat -A PREROUTING -i $iiface -p tcp --dport $sport -j DNAT --to-destination $dip:$dport
|
||||
iptables -t nat -A PREROUTING -i $iiface -p udp --dport $sport -j DNAT --to-destination $dip:$dport
|
||||
iptables -t nat -A PREROUTING -i $oiface -s 10.0.3.0/24 -d $sip -p tcp --dport $sport -j DNAT --to-destination $dip:$dport
|
||||
iptables -t nat -A PREROUTING -i $oiface -s 10.0.3.0/24 -d $sip -p udp --dport $sport -j DNAT --to-destination $dip:$dport
|
||||
iptables -t nat -A POSTROUTING -o $oiface -s 10.0.3.0/24 -d $dip/32 -p tcp --dport $dport -j SNAT --to-source $sip:$sport
|
||||
iptables -t nat -A POSTROUTING -o $oiface -s 10.0.3.0/24 -d $dip/32 -p udp --dport $dport -j SNAT --to-source $sip:$sport
|
||||
|
||||
|
||||
iptables -t nat -A PREROUTING -i $iiface -s $sip/32 -d $sip -p tcp --dport $sport -j DNAT --to-destination $dip:$dport
|
||||
iptables -t nat -A PREROUTING -i $iiface -s $sip/32 -d $sip -p udp --dport $sport -j DNAT --to-destination $dip:$dport
|
||||
iptables -t nat -A POSTROUTING -o $oiface -s $sip/32 -d $dip/32 -p tcp --dport $dport -j SNAT --to-source $sip:$sport
|
||||
iptables -t nat -A POSTROUTING -o $oiface -s $sip/32 -d $dip/32 -p udp --dport $dport -j SNAT --to-source $sip:$sport
|
||||
2
container-runtime/package-lock.json
generated
2
container-runtime/package-lock.json
generated
@@ -37,7 +37,7 @@
|
||||
},
|
||||
"../sdk/dist": {
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.4.0-beta.20",
|
||||
"version": "0.4.0-beta.23",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@iarna/toml": "^3.0.0",
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { types as T, utils } from "@start9labs/start-sdk"
|
||||
import {
|
||||
ExtendedVersion,
|
||||
types as T,
|
||||
utils,
|
||||
VersionRange,
|
||||
} from "@start9labs/start-sdk"
|
||||
import * as net from "net"
|
||||
import { object, string, number, literals, some, unknown } from "ts-matches"
|
||||
import { Effects } from "../Models/Effects"
|
||||
@@ -133,22 +138,20 @@ export function makeEffects(context: EffectContext): Effects {
|
||||
...options,
|
||||
}) as ReturnType<T.Effects["action"]["getInput"]>
|
||||
},
|
||||
request(...[options]: Parameters<T.Effects["action"]["request"]>) {
|
||||
return rpcRound("action.request", {
|
||||
createTask(...[options]: Parameters<T.Effects["action"]["createTask"]>) {
|
||||
return rpcRound("action.create-task", {
|
||||
...options,
|
||||
}) as ReturnType<T.Effects["action"]["request"]>
|
||||
}) as ReturnType<T.Effects["action"]["createTask"]>
|
||||
},
|
||||
run(...[options]: Parameters<T.Effects["action"]["run"]>) {
|
||||
return rpcRound("action.run", {
|
||||
...options,
|
||||
}) as ReturnType<T.Effects["action"]["run"]>
|
||||
},
|
||||
clearRequests(
|
||||
...[options]: Parameters<T.Effects["action"]["clearRequests"]>
|
||||
) {
|
||||
return rpcRound("action.clear-requests", {
|
||||
clearTasks(...[options]: Parameters<T.Effects["action"]["clearTasks"]>) {
|
||||
return rpcRound("action.clear-tasks", {
|
||||
...options,
|
||||
}) as ReturnType<T.Effects["action"]["clearRequests"]>
|
||||
}) as ReturnType<T.Effects["action"]["clearTasks"]>
|
||||
},
|
||||
},
|
||||
bind(...[options]: Parameters<T.Effects["bind"]>) {
|
||||
|
||||
@@ -12,9 +12,15 @@ import {
|
||||
any,
|
||||
shape,
|
||||
anyOf,
|
||||
literals,
|
||||
} from "ts-matches"
|
||||
|
||||
import { types as T, utils } from "@start9labs/start-sdk"
|
||||
import {
|
||||
ExtendedVersion,
|
||||
types as T,
|
||||
utils,
|
||||
VersionRange,
|
||||
} from "@start9labs/start-sdk"
|
||||
import * as fs from "fs"
|
||||
|
||||
import { CallbackHolder } from "../Models/CallbackHolder"
|
||||
@@ -80,6 +86,10 @@ const callbackType = object({
|
||||
const initType = object({
|
||||
id: idType.optional(),
|
||||
method: literal("init"),
|
||||
params: object({
|
||||
id: string,
|
||||
kind: literals("install", "update", "restore").nullable(),
|
||||
}),
|
||||
})
|
||||
const startType = object({
|
||||
id: idType.optional(),
|
||||
@@ -92,6 +102,10 @@ const stopType = object({
|
||||
const exitType = object({
|
||||
id: idType.optional(),
|
||||
method: literal("exit"),
|
||||
params: object({
|
||||
id: string,
|
||||
target: string.nullable(),
|
||||
}),
|
||||
})
|
||||
const evalType = object({
|
||||
id: idType.optional(),
|
||||
@@ -276,15 +290,29 @@ export class RpcListener {
|
||||
this.system.stop().then((result) => ({ result })),
|
||||
)
|
||||
})
|
||||
.when(exitType, async ({ id }) => {
|
||||
.when(exitType, async ({ id, params }) => {
|
||||
return handleRpc(
|
||||
id,
|
||||
(async () => {
|
||||
if (this._system) await this._system.exit()
|
||||
if (this._system) {
|
||||
let target = null
|
||||
if (params.target)
|
||||
try {
|
||||
target = ExtendedVersion.parse(params.target)
|
||||
} catch (_) {
|
||||
target = VersionRange.parse(params.target).normalize()
|
||||
}
|
||||
await this._system.exit(
|
||||
makeEffects({
|
||||
procedureId: params.id,
|
||||
}),
|
||||
target,
|
||||
)
|
||||
}
|
||||
})().then((result) => ({ result })),
|
||||
)
|
||||
})
|
||||
.when(initType, async ({ id }) => {
|
||||
.when(initType, async ({ id, params }) => {
|
||||
return handleRpc(
|
||||
id,
|
||||
(async () => {
|
||||
@@ -292,15 +320,16 @@ export class RpcListener {
|
||||
const system = await this.getDependencies.system()
|
||||
this.callbacks = new CallbackHolder(
|
||||
makeEffects({
|
||||
procedureId: null,
|
||||
procedureId: params.id,
|
||||
}),
|
||||
)
|
||||
const callbacks = this.callbacks.child("containerInit")
|
||||
await system.containerInit(
|
||||
const callbacks = this.callbacks.child("init")
|
||||
await system.init(
|
||||
makeEffects({
|
||||
procedureId: null,
|
||||
procedureId: params.id,
|
||||
callbacks,
|
||||
}),
|
||||
params.kind,
|
||||
)
|
||||
this._system = system
|
||||
}
|
||||
@@ -387,16 +416,6 @@ export class RpcListener {
|
||||
switch (procedure) {
|
||||
case "/backup/create":
|
||||
return system.createBackup(effects, timeout || null)
|
||||
case "/backup/restore":
|
||||
return system.restoreBackup(effects, timeout || null)
|
||||
case "/packageInit":
|
||||
return system.packageInit(effects, timeout || null)
|
||||
case "/packageUninit":
|
||||
return system.packageUninit(
|
||||
effects,
|
||||
string.optional().unsafeCast(input),
|
||||
timeout || null,
|
||||
)
|
||||
default:
|
||||
const procedures = unNestPath(procedure)
|
||||
switch (true) {
|
||||
|
||||
@@ -7,17 +7,20 @@ import { Volume } from "./matchVolume"
|
||||
import {
|
||||
CommandOptions,
|
||||
ExecOptions,
|
||||
ExecSpawnable,
|
||||
SubContainerOwned,
|
||||
} from "@start9labs/start-sdk/package/lib/util/SubContainer"
|
||||
import { Mounts } from "@start9labs/start-sdk/package/lib/mainFn/Mounts"
|
||||
import { Manifest } from "@start9labs/start-sdk/base/lib/osBindings"
|
||||
import { BackupEffects } from "@start9labs/start-sdk/package/lib/backup/Backups"
|
||||
import { Drop } from "@start9labs/start-sdk/package/lib/util"
|
||||
import { SDKManifest } from "@start9labs/start-sdk/base/lib/types"
|
||||
export const exec = promisify(cp.exec)
|
||||
export const execFile = promisify(cp.execFile)
|
||||
|
||||
export class DockerProcedureContainer extends Drop {
|
||||
private constructor(private readonly subcontainer: ExecSpawnable) {
|
||||
private constructor(
|
||||
private readonly subcontainer: SubContainer<SDKManifest>,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@@ -27,7 +30,7 @@ export class DockerProcedureContainer extends Drop {
|
||||
data: DockerProcedure,
|
||||
volumes: { [id: VolumeId]: Volume },
|
||||
name: string,
|
||||
options: { subcontainer?: ExecSpawnable } = {},
|
||||
options: { subcontainer?: SubContainer<SDKManifest> } = {},
|
||||
) {
|
||||
const subcontainer =
|
||||
options?.subcontainer ??
|
||||
@@ -47,7 +50,7 @@ export class DockerProcedureContainer extends Drop {
|
||||
volumes: { [id: VolumeId]: Volume },
|
||||
name: string,
|
||||
) {
|
||||
const subcontainer = await SubContainer.of(
|
||||
const subcontainer = await SubContainerOwned.of(
|
||||
effects as BackupEffects,
|
||||
{ imageId: data.image },
|
||||
null,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Effects } from "../../../Models/Effects"
|
||||
import { off } from "node:process"
|
||||
import { CommandController } from "@start9labs/start-sdk/package/lib/mainFn/CommandController"
|
||||
import { SDKManifest } from "@start9labs/start-sdk/base/lib/types"
|
||||
import { SubContainerRc } from "@start9labs/start-sdk/package/lib/util/SubContainer"
|
||||
|
||||
const EMBASSY_HEALTH_INTERVAL = 15 * 1000
|
||||
const EMBASSY_PROPERTIES_LOOP = 30 * 1000
|
||||
@@ -16,8 +17,11 @@ const EMBASSY_PROPERTIES_LOOP = 30 * 1000
|
||||
* Also, this has an ability to clean itself up too if need be.
|
||||
*/
|
||||
export class MainLoop {
|
||||
private subcontainerRc?: SubContainerRc<SDKManifest>
|
||||
get mainSubContainerHandle() {
|
||||
return this.mainEvent?.daemon?.subContainerHandle
|
||||
this.subcontainerRc =
|
||||
this.subcontainerRc ?? this.mainEvent?.daemon?.subcontainerRc()
|
||||
return this.subcontainerRc
|
||||
}
|
||||
private healthLoops?: {
|
||||
name: string
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {
|
||||
ExtendedVersion,
|
||||
FileHelper,
|
||||
getDataVersion,
|
||||
overlaps,
|
||||
types as T,
|
||||
utils,
|
||||
VersionRange,
|
||||
@@ -310,7 +312,13 @@ export class SystemForEmbassy implements System {
|
||||
readonly moduleCode: Partial<U.ExpectedExports>,
|
||||
) {}
|
||||
|
||||
async containerInit(effects: Effects): Promise<void> {
|
||||
async init(
|
||||
effects: Effects,
|
||||
kind: "install" | "update" | "restore" | null,
|
||||
): Promise<void> {
|
||||
if (kind === "restore") {
|
||||
await this.restoreBackup(effects, null)
|
||||
}
|
||||
for (let depId in this.manifest.dependencies) {
|
||||
if (this.manifest.dependencies[depId]?.config) {
|
||||
await this.dependenciesAutoconfig(effects, depId, null)
|
||||
@@ -320,6 +328,9 @@ export class SystemForEmbassy implements System {
|
||||
await this.exportActions(effects)
|
||||
await this.exportNetwork(effects)
|
||||
await this.containerSetDependencies(effects)
|
||||
if (kind === "install" || kind === "update") {
|
||||
await this.packageInit(effects, null)
|
||||
}
|
||||
}
|
||||
async containerSetDependencies(effects: T.Effects) {
|
||||
const oldDeps: Record<string, string[]> = Object.fromEntries(
|
||||
@@ -359,17 +370,23 @@ export class SystemForEmbassy implements System {
|
||||
}
|
||||
|
||||
async packageInit(effects: Effects, timeoutMs: number | null): Promise<void> {
|
||||
const previousVersion = await effects.getDataVersion()
|
||||
const previousVersion = await getDataVersion(effects)
|
||||
if (previousVersion) {
|
||||
if (
|
||||
(await this.migration(effects, { from: previousVersion }, timeoutMs))
|
||||
.configured
|
||||
) {
|
||||
await effects.action.clearRequests({ only: ["needs-config"] })
|
||||
const migrationRes = await this.migration(
|
||||
effects,
|
||||
{ from: previousVersion },
|
||||
timeoutMs,
|
||||
)
|
||||
if (migrationRes) {
|
||||
if (migrationRes.configured)
|
||||
await effects.action.clearTasks({ only: ["needs-config"] })
|
||||
await configFile.write(
|
||||
effects,
|
||||
await this.getConfig(effects, timeoutMs),
|
||||
)
|
||||
}
|
||||
await configFile.write(effects, await this.getConfig(effects, timeoutMs))
|
||||
} else if (this.manifest.config) {
|
||||
await effects.action.request({
|
||||
await effects.action.createTask({
|
||||
packageId: this.manifest.id,
|
||||
actionId: "config",
|
||||
severity: "critical",
|
||||
@@ -460,7 +477,7 @@ export class SystemForEmbassy implements System {
|
||||
masked: false,
|
||||
path: "",
|
||||
schemeOverride: null,
|
||||
search: {},
|
||||
query: {},
|
||||
username: null,
|
||||
}),
|
||||
])
|
||||
@@ -562,14 +579,17 @@ export class SystemForEmbassy implements System {
|
||||
}
|
||||
await effects.action.clear({ except: Object.keys(actions) })
|
||||
}
|
||||
async packageUninit(
|
||||
async uninit(
|
||||
effects: Effects,
|
||||
nextVersion: Optional<string>,
|
||||
timeoutMs: number | null,
|
||||
target: ExtendedVersion | VersionRange | null,
|
||||
timeoutMs?: number | null,
|
||||
): Promise<void> {
|
||||
await this.currentRunning?.clean({ timeout: timeoutMs ?? undefined })
|
||||
if (nextVersion) {
|
||||
await this.migration(effects, { to: nextVersion }, timeoutMs)
|
||||
if (
|
||||
target &&
|
||||
!overlaps(target, ExtendedVersion.parseEmver(this.manifest.version))
|
||||
) {
|
||||
await this.migration(effects, { to: target }, timeoutMs ?? null)
|
||||
}
|
||||
await effects.setMainStatus({ status: "stopped" })
|
||||
}
|
||||
@@ -781,31 +801,31 @@ export class SystemForEmbassy implements System {
|
||||
|
||||
async migration(
|
||||
effects: Effects,
|
||||
version: { from: string } | { to: string },
|
||||
version:
|
||||
| { from: VersionRange | ExtendedVersion }
|
||||
| { to: VersionRange | ExtendedVersion },
|
||||
timeoutMs: number | null,
|
||||
): Promise<{ configured: boolean }> {
|
||||
): Promise<{ configured: boolean } | null> {
|
||||
let migration
|
||||
let args: [string, ...string[]]
|
||||
if ("from" in version) {
|
||||
args = [version.from, "from"]
|
||||
const fromExver = ExtendedVersion.parse(version.from)
|
||||
args = [version.from.toString(), "from"]
|
||||
if (!this.manifest.migrations) return { configured: true }
|
||||
migration = Object.entries(this.manifest.migrations.from)
|
||||
.map(
|
||||
([version, procedure]) =>
|
||||
[VersionRange.parseEmver(version), procedure] as const,
|
||||
)
|
||||
.find(([versionEmver, _]) => versionEmver.satisfiedBy(fromExver))
|
||||
.find(([versionEmver, _]) => overlaps(versionEmver, version.from))
|
||||
} else {
|
||||
args = [version.to, "to"]
|
||||
const toExver = ExtendedVersion.parse(version.to)
|
||||
args = [version.to.toString(), "to"]
|
||||
if (!this.manifest.migrations) return { configured: true }
|
||||
migration = Object.entries(this.manifest.migrations.to)
|
||||
.map(
|
||||
([version, procedure]) =>
|
||||
[VersionRange.parseEmver(version), procedure] as const,
|
||||
)
|
||||
.find(([versionEmver, _]) => versionEmver.satisfiedBy(toExver))
|
||||
.find(([versionEmver, _]) => overlaps(versionEmver, version.to))
|
||||
}
|
||||
|
||||
if (migration) {
|
||||
@@ -841,7 +861,7 @@ export class SystemForEmbassy implements System {
|
||||
})) as any
|
||||
}
|
||||
}
|
||||
return { configured: true }
|
||||
return null
|
||||
}
|
||||
async properties(
|
||||
effects: Effects,
|
||||
@@ -1022,7 +1042,7 @@ export class SystemForEmbassy implements System {
|
||||
})) as any
|
||||
const diff = partialDiff(oldConfig, newConfig)
|
||||
if (diff) {
|
||||
await effects.action.request({
|
||||
await effects.action.createTask({
|
||||
actionId: "config",
|
||||
packageId: id,
|
||||
replayId: `${id}/config`,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { System } from "../../Interfaces/System"
|
||||
import { Effects } from "../../Models/Effects"
|
||||
import { T, utils } from "@start9labs/start-sdk"
|
||||
import { ExtendedVersion, T, utils, VersionRange } from "@start9labs/start-sdk"
|
||||
|
||||
export const STARTOS_JS_LOCATION = "/usr/lib/startos/package/index.js"
|
||||
|
||||
@@ -10,6 +10,7 @@ type RunningMain = {
|
||||
|
||||
export class SystemForStartOs implements System {
|
||||
private runningMain: RunningMain | undefined
|
||||
private starting: boolean = false
|
||||
|
||||
static of() {
|
||||
return new SystemForStartOs(require(STARTOS_JS_LOCATION))
|
||||
@@ -18,22 +19,23 @@ export class SystemForStartOs implements System {
|
||||
constructor(readonly abi: T.ABI) {
|
||||
this
|
||||
}
|
||||
async containerInit(effects: Effects): Promise<void> {
|
||||
return void (await this.abi.containerInit({ effects }))
|
||||
}
|
||||
async packageInit(
|
||||
|
||||
async init(
|
||||
effects: Effects,
|
||||
kind: "install" | "update" | "restore" | null,
|
||||
): Promise<void> {
|
||||
return void (await this.abi.init({ effects, kind }))
|
||||
}
|
||||
|
||||
async exit(
|
||||
effects: Effects,
|
||||
target: ExtendedVersion | VersionRange | null,
|
||||
timeoutMs: number | null = null,
|
||||
): Promise<void> {
|
||||
return void (await this.abi.packageInit({ effects }))
|
||||
}
|
||||
async packageUninit(
|
||||
effects: Effects,
|
||||
nextVersion: string | null = null,
|
||||
timeoutMs: number | null = null,
|
||||
): Promise<void> {
|
||||
return void (await this.abi.packageUninit({ effects, nextVersion }))
|
||||
// TODO: stop?
|
||||
return void (await this.abi.uninit({ effects, target }))
|
||||
}
|
||||
|
||||
async createBackup(
|
||||
effects: T.Effects,
|
||||
timeoutMs: number | null,
|
||||
@@ -42,14 +44,6 @@ export class SystemForStartOs implements System {
|
||||
effects,
|
||||
}))
|
||||
}
|
||||
async restoreBackup(
|
||||
effects: T.Effects,
|
||||
timeoutMs: number | null,
|
||||
): Promise<void> {
|
||||
return void (await this.abi.restoreBackup({
|
||||
effects,
|
||||
}))
|
||||
}
|
||||
getActionInput(
|
||||
effects: Effects,
|
||||
id: string,
|
||||
@@ -70,28 +64,31 @@ export class SystemForStartOs implements System {
|
||||
return action.run({ effects, input })
|
||||
}
|
||||
|
||||
async exit(): Promise<void> {}
|
||||
|
||||
async start(effects: Effects): Promise<void> {
|
||||
if (this.runningMain) return
|
||||
effects.constRetry = utils.once(() => effects.restart())
|
||||
let mainOnTerm: () => Promise<void> | undefined
|
||||
const started = async (onTerm: () => Promise<void>) => {
|
||||
await effects.setMainStatus({ status: "running" })
|
||||
mainOnTerm = onTerm
|
||||
return null
|
||||
}
|
||||
const daemons = await (
|
||||
await this.abi.main({
|
||||
effects,
|
||||
started,
|
||||
})
|
||||
).build()
|
||||
this.runningMain = {
|
||||
stop: async () => {
|
||||
if (mainOnTerm) await mainOnTerm()
|
||||
await daemons.term()
|
||||
},
|
||||
try {
|
||||
if (this.runningMain || this.starting) return
|
||||
this.starting = true
|
||||
effects.constRetry = utils.once(() => effects.restart())
|
||||
let mainOnTerm: () => Promise<void> | undefined
|
||||
const started = async (onTerm: () => Promise<void>) => {
|
||||
await effects.setMainStatus({ status: "running" })
|
||||
mainOnTerm = onTerm
|
||||
return null
|
||||
}
|
||||
const daemons = await (
|
||||
await this.abi.main({
|
||||
effects,
|
||||
started,
|
||||
})
|
||||
).build()
|
||||
this.runningMain = {
|
||||
stop: async () => {
|
||||
if (mainOnTerm) await mainOnTerm()
|
||||
await daemons.term()
|
||||
},
|
||||
}
|
||||
} finally {
|
||||
this.starting = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { types as T } from "@start9labs/start-sdk"
|
||||
import {
|
||||
ExtendedVersion,
|
||||
types as T,
|
||||
VersionRange,
|
||||
} from "@start9labs/start-sdk"
|
||||
import { Effects } from "../Models/Effects"
|
||||
import { CallbackHolder } from "../Models/CallbackHolder"
|
||||
import { Optional } from "ts-matches/lib/parsers/interfaces"
|
||||
|
||||
export type Procedure =
|
||||
| "/packageInit"
|
||||
| "/packageUninit"
|
||||
| "/backup/create"
|
||||
| "/backup/restore"
|
||||
| `/actions/${string}/getInput`
|
||||
| `/actions/${string}/run`
|
||||
|
||||
@@ -15,20 +15,15 @@ export type ExecuteResult =
|
||||
| { ok: unknown }
|
||||
| { err: { code: number; message: string } }
|
||||
export type System = {
|
||||
containerInit(effects: T.Effects): Promise<void>
|
||||
init(
|
||||
effects: T.Effects,
|
||||
kind: "install" | "update" | "restore" | null,
|
||||
): Promise<void>
|
||||
|
||||
start(effects: T.Effects): Promise<void>
|
||||
stop(): Promise<void>
|
||||
|
||||
packageInit(effects: Effects, timeoutMs: number | null): Promise<void>
|
||||
packageUninit(
|
||||
effects: Effects,
|
||||
nextVersion: Optional<string>,
|
||||
timeoutMs: number | null,
|
||||
): Promise<void>
|
||||
|
||||
createBackup(effects: T.Effects, timeoutMs: number | null): Promise<void>
|
||||
restoreBackup(effects: T.Effects, timeoutMs: number | null): Promise<void>
|
||||
runAction(
|
||||
effects: Effects,
|
||||
actionId: string,
|
||||
@@ -41,7 +36,10 @@ export type System = {
|
||||
timeoutMs: number | null,
|
||||
): Promise<T.ActionInput | null>
|
||||
|
||||
exit(): Promise<void>
|
||||
exit(
|
||||
effects: Effects,
|
||||
target: ExtendedVersion | VersionRange | null,
|
||||
): Promise<void>
|
||||
}
|
||||
|
||||
export type RunningMain = {
|
||||
|
||||
@@ -4,25 +4,15 @@ use crate::ActionId;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ProcedureName {
|
||||
GetConfig,
|
||||
SetConfig,
|
||||
CreateBackup,
|
||||
RestoreBackup,
|
||||
GetActionInput(ActionId),
|
||||
RunAction(ActionId),
|
||||
PackageInit,
|
||||
PackageUninit,
|
||||
}
|
||||
|
||||
impl ProcedureName {
|
||||
pub fn js_function_name(&self) -> String {
|
||||
match self {
|
||||
ProcedureName::PackageInit => "/packageInit".to_string(),
|
||||
ProcedureName::PackageUninit => "/packageUninit".to_string(),
|
||||
ProcedureName::SetConfig => "/config/set".to_string(),
|
||||
ProcedureName::GetConfig => "/config/get".to_string(),
|
||||
ProcedureName::CreateBackup => "/backup/create".to_string(),
|
||||
ProcedureName::RestoreBackup => "/backup/restore".to_string(),
|
||||
ProcedureName::RunAction(id) => format!("/actions/{}/run", id),
|
||||
ProcedureName::GetActionInput(id) => format!("/actions/{}/getInput", id),
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::fmt;
|
||||
|
||||
use clap::{CommandFactory, FromArgMatches, Parser};
|
||||
pub use models::ActionId;
|
||||
use models::PackageId;
|
||||
use models::{PackageId, ReplayId};
|
||||
use qrcode::QrCode;
|
||||
use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -10,6 +10,7 @@ use tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::package::TaskSeverity;
|
||||
use crate::prelude::*;
|
||||
use crate::rpc_continuations::Guid;
|
||||
use crate::util::serde::{
|
||||
@@ -38,6 +39,13 @@ pub fn action_api<C: Context>() -> ParentHandler<C> {
|
||||
.with_about("Run service action")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand(
|
||||
"clear-task",
|
||||
from_fn_async(clear_task)
|
||||
.no_display()
|
||||
.with_about("Clear a service task")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS)]
|
||||
@@ -332,3 +340,45 @@ pub async fn run_action(
|
||||
.await
|
||||
.map(|res| res.map(ActionResult::upcast))
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[command(rename_all = "kebab-case")]
|
||||
pub struct ClearTaskParams {
|
||||
pub package_id: PackageId,
|
||||
pub replay_id: ReplayId,
|
||||
#[arg(long)]
|
||||
pub force: bool,
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn clear_task(
|
||||
ctx: RpcContext,
|
||||
ClearTaskParams {
|
||||
package_id,
|
||||
replay_id,
|
||||
force,
|
||||
}: ClearTaskParams,
|
||||
) -> Result<(), Error> {
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
if let Some(task) = db
|
||||
.as_public_mut()
|
||||
.as_package_data_mut()
|
||||
.as_idx_mut(&package_id)
|
||||
.or_not_found(&package_id)?
|
||||
.as_tasks_mut()
|
||||
.remove(&replay_id)?
|
||||
{
|
||||
if !force && task.as_task().as_severity().de()? == TaskSeverity::Critical {
|
||||
return Err(Error::new(
|
||||
eyre!("Cannot clear critical task"),
|
||||
ErrorKind::InvalidRequest,
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
.result
|
||||
}
|
||||
|
||||
@@ -44,22 +44,25 @@ impl Map for Sessions {
|
||||
}
|
||||
|
||||
pub async fn write_shadow(password: &str) -> Result<(), Error> {
|
||||
let hash: String = sha_crypt::sha512_simple(password, &sha_crypt::Sha512Params::default())
|
||||
.map_err(|e| Error::new(eyre!("{e:?}"), ErrorKind::Serialization))?;
|
||||
let shadow_contents = tokio::fs::read_to_string("/etc/shadow").await?;
|
||||
let mut shadow_file =
|
||||
create_file_mod("/media/startos/config/overlay/etc/shadow", 0o640).await?;
|
||||
for line in shadow_contents.lines() {
|
||||
if line.starts_with("start9:") {
|
||||
let rest = line.splitn(3, ":").nth(2).ok_or_else(|| {
|
||||
Error::new(eyre!("malformed /etc/shadow"), ErrorKind::ParseSysInfo)
|
||||
})?;
|
||||
let pw = sha_crypt::sha512_simple(password, &sha_crypt::Sha512Params::default())
|
||||
.map_err(|e| Error::new(eyre!("{e:?}"), ErrorKind::Serialization))?;
|
||||
shadow_file
|
||||
.write_all(format!("start9:{pw}:{rest}\n").as_bytes())
|
||||
.await?;
|
||||
} else {
|
||||
shadow_file.write_all(line.as_bytes()).await?;
|
||||
shadow_file.write_all(b"\n").await?;
|
||||
match line.split_once(":") {
|
||||
Some((user, rest)) if user == "start9" || user == "kiosk" => {
|
||||
let (_, rest) = rest.split_once(":").ok_or_else(|| {
|
||||
Error::new(eyre!("malformed /etc/shadow"), ErrorKind::ParseSysInfo)
|
||||
})?;
|
||||
shadow_file
|
||||
.write_all(format!("{user}:{hash}:{rest}\n").as_bytes())
|
||||
.await?;
|
||||
}
|
||||
_ => {
|
||||
shadow_file.write_all(line.as_bytes()).await?;
|
||||
shadow_file.write_all(b"\n").await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
shadow_file.sync_all().await?;
|
||||
|
||||
@@ -23,7 +23,9 @@ use crate::prelude::*;
|
||||
use crate::s9pk::S9pk;
|
||||
use crate::service::service_map::DownloadInstallFuture;
|
||||
use crate::setup::SetupExecuteProgress;
|
||||
use crate::system::sync_kiosk;
|
||||
use crate::util::serde::IoFormat;
|
||||
use crate::PLATFORM;
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -80,6 +82,7 @@ pub async fn recover_full_embassy(
|
||||
recovery_source: TmpMountGuard,
|
||||
server_id: &str,
|
||||
recovery_password: &str,
|
||||
kiosk: Option<bool>,
|
||||
SetupExecuteProgress {
|
||||
init_phases,
|
||||
restore_phase,
|
||||
@@ -105,8 +108,12 @@ pub async fn recover_full_embassy(
|
||||
)
|
||||
.with_kind(ErrorKind::PasswordHashGeneration)?;
|
||||
|
||||
let kiosk = Some(kiosk.unwrap_or(true)).filter(|_| &*PLATFORM != "raspberrypi");
|
||||
sync_kiosk(kiosk).await?;
|
||||
|
||||
let db = ctx.db().await?;
|
||||
db.put(&ROOT, &Database::init(&os_backup.account)?).await?;
|
||||
db.put(&ROOT, &Database::init(&os_backup.account, kiosk)?)
|
||||
.await?;
|
||||
drop(db);
|
||||
|
||||
let init_result = init(&ctx.webserver, &ctx.config, init_phases).await?;
|
||||
@@ -166,6 +173,7 @@ async fn restore_packages(
|
||||
.install(
|
||||
ctx.clone(),
|
||||
|| S9pk::open(s9pk_path, Some(&id)),
|
||||
None, // TODO: pull from metadata?
|
||||
Some(backup_dir),
|
||||
None,
|
||||
)
|
||||
|
||||
@@ -35,7 +35,7 @@ use crate::net::wifi::WpaCli;
|
||||
use crate::prelude::*;
|
||||
use crate::progress::{FullProgressTracker, PhaseProgressTrackerHandle};
|
||||
use crate::rpc_continuations::{Guid, OpenAuthedContinuations, RpcContinuations};
|
||||
use crate::service::action::update_requested_actions;
|
||||
use crate::service::action::update_tasks;
|
||||
use crate::service::effects::callbacks::ServiceCallbacks;
|
||||
use crate::service::ServiceMap;
|
||||
use crate::shutdown::Shutdown;
|
||||
@@ -102,14 +102,14 @@ impl InitRpcContextPhases {
|
||||
pub struct CleanupInitPhases {
|
||||
cleanup_sessions: PhaseProgressTrackerHandle,
|
||||
init_services: PhaseProgressTrackerHandle,
|
||||
check_requested_actions: PhaseProgressTrackerHandle,
|
||||
check_tasks: PhaseProgressTrackerHandle,
|
||||
}
|
||||
impl CleanupInitPhases {
|
||||
pub fn new(handle: &FullProgressTracker) -> Self {
|
||||
Self {
|
||||
cleanup_sessions: handle.add_phase("Cleaning up sessions".into(), Some(1)),
|
||||
init_services: handle.add_phase("Initializing services".into(), Some(10)),
|
||||
check_requested_actions: handle.add_phase("Checking action requests".into(), Some(1)),
|
||||
check_tasks: handle.add_phase("Checking action requests".into(), Some(1)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,7 +307,7 @@ impl RpcContext {
|
||||
CleanupInitPhases {
|
||||
mut cleanup_sessions,
|
||||
init_services,
|
||||
mut check_requested_actions,
|
||||
mut check_tasks,
|
||||
}: CleanupInitPhases,
|
||||
) -> Result<(), Error> {
|
||||
cleanup_sessions.start();
|
||||
@@ -369,31 +369,27 @@ impl RpcContext {
|
||||
tracing::info!("Initialized Services");
|
||||
|
||||
// TODO
|
||||
check_requested_actions.start();
|
||||
check_tasks.start();
|
||||
let peek = self.db.peek().await;
|
||||
let mut action_input: OrdMap<PackageId, BTreeMap<ActionId, Value>> = OrdMap::new();
|
||||
let requested_actions: BTreeSet<_> = peek
|
||||
let tasks: BTreeSet<_> = peek
|
||||
.as_public()
|
||||
.as_package_data()
|
||||
.as_entries()?
|
||||
.into_iter()
|
||||
.map(|(_, pde)| {
|
||||
Ok(pde
|
||||
.as_requested_actions()
|
||||
.as_entries()?
|
||||
.into_iter()
|
||||
.map(|(_, r)| {
|
||||
Ok::<_, Error>((
|
||||
r.as_request().as_package_id().de()?,
|
||||
r.as_request().as_action_id().de()?,
|
||||
))
|
||||
}))
|
||||
Ok(pde.as_tasks().as_entries()?.into_iter().map(|(_, r)| {
|
||||
Ok::<_, Error>((
|
||||
r.as_task().as_package_id().de()?,
|
||||
r.as_task().as_action_id().de()?,
|
||||
))
|
||||
}))
|
||||
})
|
||||
.flatten_ok()
|
||||
.map(|a| a.and_then(|a| a))
|
||||
.try_collect()?;
|
||||
let procedure_id = Guid::new();
|
||||
for (package_id, action_id) in requested_actions {
|
||||
for (package_id, action_id) in tasks {
|
||||
if let Some(service) = self.services.get(&package_id).await.as_ref() {
|
||||
if let Some(input) = service
|
||||
.get_action_input(procedure_id.clone(), action_id.clone())
|
||||
@@ -412,14 +408,8 @@ impl RpcContext {
|
||||
for (package_id, action_input) in &action_input {
|
||||
for (action_id, input) in action_input {
|
||||
for (_, pde) in db.as_public_mut().as_package_data_mut().as_entries_mut()? {
|
||||
pde.as_requested_actions_mut().mutate(|requested_actions| {
|
||||
Ok(update_requested_actions(
|
||||
requested_actions,
|
||||
package_id,
|
||||
action_id,
|
||||
input,
|
||||
false,
|
||||
))
|
||||
pde.as_tasks_mut().mutate(|tasks| {
|
||||
Ok(update_tasks(tasks, package_id, action_id, input, false))
|
||||
})?;
|
||||
}
|
||||
}
|
||||
@@ -428,7 +418,7 @@ impl RpcContext {
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
check_requested_actions.complete();
|
||||
check_tasks.complete();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ pub struct Database {
|
||||
pub private: Private,
|
||||
}
|
||||
impl Database {
|
||||
pub fn init(account: &AccountInfo) -> Result<Self, Error> {
|
||||
pub fn init(account: &AccountInfo, kiosk: Option<bool>) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
public: Public::init(account)?,
|
||||
public: Public::init(account, kiosk)?,
|
||||
private: Private {
|
||||
key_store: KeyStore::new(account)?,
|
||||
password: account.password.clone(),
|
||||
|
||||
@@ -3,10 +3,7 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||
use chrono::{DateTime, Utc};
|
||||
use exver::VersionRange;
|
||||
use imbl_value::InternedString;
|
||||
use models::{
|
||||
ActionId, DataUrl, HealthCheckId, HostId, PackageId, ReplayId, ServiceInterfaceId,
|
||||
VersionString,
|
||||
};
|
||||
use models::{ActionId, DataUrl, HealthCheckId, HostId, PackageId, ReplayId, ServiceInterfaceId};
|
||||
use patch_db::json_ptr::JsonPointer;
|
||||
use patch_db::HasModel;
|
||||
use reqwest::Url;
|
||||
@@ -365,7 +362,7 @@ impl Default for ActionVisibility {
|
||||
#[ts(export)]
|
||||
pub struct PackageDataEntry {
|
||||
pub state_info: PackageState,
|
||||
pub data_version: Option<VersionString>,
|
||||
pub data_version: Option<String>,
|
||||
pub status: MainStatus,
|
||||
#[ts(type = "string | null")]
|
||||
pub registry: Option<Url>,
|
||||
@@ -376,8 +373,8 @@ pub struct PackageDataEntry {
|
||||
pub last_backup: Option<DateTime<Utc>>,
|
||||
pub current_dependencies: CurrentDependencies,
|
||||
pub actions: BTreeMap<ActionId, ActionMetadata>,
|
||||
#[ts(as = "BTreeMap::<String, ActionRequestEntry>")]
|
||||
pub requested_actions: BTreeMap<ReplayId, ActionRequestEntry>,
|
||||
#[ts(as = "BTreeMap::<String, TaskEntry>")]
|
||||
pub tasks: BTreeMap<ReplayId, TaskEntry>,
|
||||
pub service_interfaces: BTreeMap<ServiceInterfaceId, ServiceInterface>,
|
||||
pub hosts: Hosts,
|
||||
#[ts(type = "string[]")]
|
||||
@@ -444,8 +441,8 @@ pub enum CurrentDependencyKind {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct ActionRequestEntry {
|
||||
pub request: ActionRequest,
|
||||
pub struct TaskEntry {
|
||||
pub task: Task,
|
||||
pub active: bool,
|
||||
}
|
||||
|
||||
@@ -453,58 +450,59 @@ pub struct ActionRequestEntry {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct ActionRequest {
|
||||
pub struct Task {
|
||||
pub package_id: PackageId,
|
||||
pub action_id: ActionId,
|
||||
#[serde(default)]
|
||||
pub severity: ActionSeverity,
|
||||
pub severity: TaskSeverity,
|
||||
#[ts(optional)]
|
||||
pub reason: Option<String>,
|
||||
#[ts(optional)]
|
||||
pub when: Option<ActionRequestTrigger>,
|
||||
pub when: Option<TaskTrigger>,
|
||||
#[ts(optional)]
|
||||
pub input: Option<ActionRequestInput>,
|
||||
pub input: Option<TaskInput>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[ts(export)]
|
||||
pub enum ActionSeverity {
|
||||
Critical,
|
||||
pub enum TaskSeverity {
|
||||
Optional,
|
||||
Important,
|
||||
Critical,
|
||||
}
|
||||
impl Default for ActionSeverity {
|
||||
impl Default for TaskSeverity {
|
||||
fn default() -> Self {
|
||||
ActionSeverity::Important
|
||||
TaskSeverity::Important
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct ActionRequestTrigger {
|
||||
pub struct TaskTrigger {
|
||||
#[serde(default)]
|
||||
pub once: bool,
|
||||
pub condition: ActionRequestCondition,
|
||||
pub condition: TaskCondition,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[ts(export)]
|
||||
pub enum ActionRequestCondition {
|
||||
pub enum TaskCondition {
|
||||
InputNotMatches,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum ActionRequestInput {
|
||||
pub enum TaskInput {
|
||||
Partial {
|
||||
#[ts(type = "Record<string, unknown>")]
|
||||
value: Value,
|
||||
},
|
||||
}
|
||||
impl ActionRequestInput {
|
||||
impl TaskInput {
|
||||
pub fn matches(&self, input: Option<&Value>) -> bool {
|
||||
match self {
|
||||
Self::Partial { value } => match input {
|
||||
|
||||
@@ -40,7 +40,7 @@ pub struct Public {
|
||||
pub ui: Value,
|
||||
}
|
||||
impl Public {
|
||||
pub fn init(account: &AccountInfo) -> Result<Self, Error> {
|
||||
pub fn init(account: &AccountInfo, kiosk: Option<bool>) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
server_info: ServerInfo {
|
||||
arch: get_arch(),
|
||||
@@ -117,6 +117,7 @@ impl Public {
|
||||
smtp: None,
|
||||
ram: 0,
|
||||
devices: Vec::new(),
|
||||
kiosk,
|
||||
},
|
||||
package_data: AllPackageData::default(),
|
||||
ui: serde_json::from_str(include_str!(concat!(
|
||||
@@ -175,6 +176,7 @@ pub struct ServerInfo {
|
||||
#[ts(type = "number")]
|
||||
pub ram: u64,
|
||||
pub devices: Vec<LshwDevice>,
|
||||
pub kiosk: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, HasModel, TS)]
|
||||
|
||||
@@ -37,7 +37,7 @@ use crate::progress::{
|
||||
use crate::rpc_continuations::{Guid, RpcContinuation};
|
||||
use crate::s9pk::v2::pack::{CONTAINER_DATADIR, CONTAINER_TOOL};
|
||||
use crate::ssh::SSH_DIR;
|
||||
use crate::system::get_mem_info;
|
||||
use crate::system::{get_mem_info, sync_kiosk};
|
||||
use crate::util::io::{create_file, IOHook};
|
||||
use crate::util::lshw::lshw;
|
||||
use crate::util::net::WebSocketExt;
|
||||
@@ -510,6 +510,7 @@ pub async fn init(
|
||||
enable_zram.complete();
|
||||
|
||||
update_server_info.start();
|
||||
sync_kiosk(server_info.as_kiosk().de()?).await?;
|
||||
let ram = get_mem_info().await?.total.0 as u64 * 1024 * 1024;
|
||||
let devices = lshw().await?;
|
||||
let status_info = ServerStatus {
|
||||
|
||||
@@ -161,6 +161,7 @@ pub async fn install(
|
||||
.install(
|
||||
ctx.clone(),
|
||||
|| asset.deserialize_s9pk_buffered(ctx.client.clone(), download_progress),
|
||||
Some(registry),
|
||||
None::<Never>,
|
||||
Some(progress_tracker),
|
||||
)
|
||||
@@ -257,6 +258,7 @@ pub async fn sideload(
|
||||
.install(
|
||||
ctx.clone(),
|
||||
|| crate::s9pk::load(file.clone(), || Ok(key.de()?.0), Some(&progress_tracker)),
|
||||
None,
|
||||
None::<Never>,
|
||||
Some(progress_tracker.clone()),
|
||||
)
|
||||
|
||||
@@ -89,6 +89,7 @@ use crate::context::{
|
||||
use crate::disk::fsck::RequiresReboot;
|
||||
use crate::net::net;
|
||||
use crate::registry::context::{RegistryContext, RegistryUrlParams};
|
||||
use crate::system::kiosk;
|
||||
use crate::util::serde::{HandlerExtSerde, WithIoFormat};
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
@@ -118,7 +119,7 @@ impl std::fmt::Display for ApiState {
|
||||
}
|
||||
|
||||
pub fn main_api<C: Context>() -> ParentHandler<C> {
|
||||
let api = ParentHandler::new()
|
||||
let mut api = ParentHandler::new()
|
||||
.subcommand(
|
||||
"git-info",
|
||||
from_fn(|_: C| version::git_info()).with_about("Display the githash of StartOS CLI"),
|
||||
@@ -198,12 +199,18 @@ pub fn main_api<C: Context>() -> ParentHandler<C> {
|
||||
"util",
|
||||
util::rpc::util::<C>().with_about("Command for calculating the blake3 hash of a file"),
|
||||
);
|
||||
if &*PLATFORM != "raspberrypi" {
|
||||
api = api.subcommand("kiosk", kiosk::<C>());
|
||||
}
|
||||
#[cfg(feature = "dev")]
|
||||
let api = api.subcommand(
|
||||
"lxc",
|
||||
lxc::dev::lxc::<C>()
|
||||
.with_about("Commands related to lxc containers i.e. create, list, remove, connect"),
|
||||
);
|
||||
{
|
||||
api = api.subcommand(
|
||||
"lxc",
|
||||
lxc::dev::lxc::<C>().with_about(
|
||||
"Commands related to lxc containers i.e. create, list, remove, connect",
|
||||
),
|
||||
);
|
||||
}
|
||||
api
|
||||
}
|
||||
|
||||
|
||||
@@ -222,8 +222,9 @@ impl LanPortForwardController {
|
||||
}
|
||||
}
|
||||
|
||||
// iptables -I FORWARD -o br-start9 -p tcp -d 172.18.0.2 --dport 8333 -j ACCEPT
|
||||
// iptables -t nat -I PREROUTING -p tcp --dport 32768 -j DNAT --to 172.18.0.2:8333
|
||||
// iptables -t nat -A POSTROUTING -s 10.59.0.0/24 ! -d 10.59.0.0/24 -j SNAT --to $ip
|
||||
// iptables -I INPUT -p udp --dport $port -j ACCEPT
|
||||
// iptables -I FORWARD -s 10.59.0.0/24 -j ACCEPT
|
||||
async fn forward(external: u16, interface: &str, target: SocketAddr) -> Result<(), Error> {
|
||||
for proto in ["tcp", "udp"] {
|
||||
Command::new("iptables")
|
||||
|
||||
@@ -6,8 +6,7 @@ use models::{ActionId, PackageId, ProcedureName, ReplayId};
|
||||
|
||||
use crate::action::{ActionInput, ActionResult};
|
||||
use crate::db::model::package::{
|
||||
ActionRequestCondition, ActionRequestEntry, ActionRequestInput, ActionVisibility,
|
||||
AllowedStatuses,
|
||||
ActionVisibility, AllowedStatuses, TaskCondition, TaskEntry, TaskInput,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use crate::rpc_continuations::Guid;
|
||||
@@ -73,21 +72,21 @@ impl Service {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_requested_actions(
|
||||
requested_actions: &mut BTreeMap<ReplayId, ActionRequestEntry>,
|
||||
pub fn update_tasks(
|
||||
tasks: &mut BTreeMap<ReplayId, TaskEntry>,
|
||||
package_id: &PackageId,
|
||||
action_id: &ActionId,
|
||||
input: &Value,
|
||||
was_run: bool,
|
||||
) {
|
||||
requested_actions.retain(|_, v| {
|
||||
if &v.request.package_id != package_id || &v.request.action_id != action_id {
|
||||
tasks.retain(|_, v| {
|
||||
if &v.task.package_id != package_id || &v.task.action_id != action_id {
|
||||
return true;
|
||||
}
|
||||
if let Some(when) = &v.request.when {
|
||||
if let Some(when) = &v.task.when {
|
||||
match &when.condition {
|
||||
ActionRequestCondition::InputNotMatches => match &v.request.input {
|
||||
Some(ActionRequestInput::Partial { value }) => {
|
||||
TaskCondition::InputNotMatches => match &v.task.input {
|
||||
Some(TaskInput::Partial { value }) => {
|
||||
if is_partial_of(value, input) {
|
||||
if when.once {
|
||||
return !was_run;
|
||||
@@ -99,10 +98,7 @@ pub fn update_requested_actions(
|
||||
}
|
||||
}
|
||||
None => {
|
||||
tracing::error!(
|
||||
"action request exists in an invalid state {:?}",
|
||||
v.request
|
||||
);
|
||||
tracing::error!("action request exists in an invalid state {:?}", v.task);
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -180,14 +176,8 @@ impl Handler<RunAction> for ServiceActor {
|
||||
.db
|
||||
.mutate(|db| {
|
||||
for (_, pde) in db.as_public_mut().as_package_data_mut().as_entries_mut()? {
|
||||
pde.as_requested_actions_mut().mutate(|requested_actions| {
|
||||
Ok(update_requested_actions(
|
||||
requested_actions,
|
||||
package_id,
|
||||
action_id,
|
||||
&input,
|
||||
true,
|
||||
))
|
||||
pde.as_tasks_mut().mutate(|tasks| {
|
||||
Ok(update_tasks(tasks, package_id, action_id, &input, true))
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -5,7 +5,7 @@ use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler};
|
||||
|
||||
use crate::action::{display_action_result, ActionInput, ActionResult};
|
||||
use crate::db::model::package::{
|
||||
ActionMetadata, ActionRequest, ActionRequestCondition, ActionRequestEntry, ActionRequestTrigger,
|
||||
ActionMetadata, Task, TaskCondition, TaskEntry, TaskSeverity, TaskTrigger,
|
||||
};
|
||||
use crate::rpc_continuations::Guid;
|
||||
use crate::service::cli::ContainerCliContext;
|
||||
@@ -34,10 +34,10 @@ pub fn action_api<C: Context>() -> ParentHandler<C> {
|
||||
.with_custom_display_fn(|args, res| Ok(display_action_result(args.params, res)))
|
||||
.with_call_remote::<ContainerCliContext>(),
|
||||
)
|
||||
.subcommand("request", from_fn_async(request_action).no_cli())
|
||||
.subcommand("create-task", from_fn_async(create_task).no_cli())
|
||||
.subcommand(
|
||||
"clear-requests",
|
||||
from_fn_async(clear_action_requests)
|
||||
"clear-tasks",
|
||||
from_fn_async(clear_tasks)
|
||||
.no_display()
|
||||
.with_call_remote::<ContainerCliContext>(),
|
||||
)
|
||||
@@ -196,29 +196,29 @@ async fn run_action(
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct RequestActionParams {
|
||||
pub struct CreateTaskParams {
|
||||
#[serde(default)]
|
||||
#[ts(skip)]
|
||||
procedure_id: Guid,
|
||||
replay_id: ReplayId,
|
||||
#[serde(flatten)]
|
||||
request: ActionRequest,
|
||||
task: Task,
|
||||
}
|
||||
async fn request_action(
|
||||
async fn create_task(
|
||||
context: EffectContext,
|
||||
RequestActionParams {
|
||||
CreateTaskParams {
|
||||
procedure_id,
|
||||
replay_id,
|
||||
request,
|
||||
}: RequestActionParams,
|
||||
task,
|
||||
}: CreateTaskParams,
|
||||
) -> Result<(), Error> {
|
||||
let context = context.deref()?;
|
||||
|
||||
let src_id = &context.seed.id;
|
||||
let active = match &request.when {
|
||||
Some(ActionRequestTrigger { once, condition }) => match condition {
|
||||
ActionRequestCondition::InputNotMatches => {
|
||||
let Some(input) = request.input.as_ref() else {
|
||||
let active = match &task.when {
|
||||
Some(TaskTrigger { once, condition }) => match condition {
|
||||
TaskCondition::InputNotMatches => {
|
||||
let Some(input) = task.input.as_ref() else {
|
||||
return Err(Error::new(
|
||||
eyre!("input-not-matches trigger requires input to be specified"),
|
||||
ErrorKind::InvalidRequest,
|
||||
@@ -228,19 +228,19 @@ async fn request_action(
|
||||
.seed
|
||||
.ctx
|
||||
.services
|
||||
.get(&request.package_id)
|
||||
.get(&task.package_id)
|
||||
.await
|
||||
.as_ref()
|
||||
{
|
||||
let Some(prev) = service
|
||||
.get_action_input(procedure_id, request.action_id.clone())
|
||||
.get_action_input(procedure_id.clone(), task.action_id.clone())
|
||||
.await?
|
||||
else {
|
||||
return Err(Error::new(
|
||||
eyre!(
|
||||
"action {} of {} has no input",
|
||||
request.action_id,
|
||||
request.package_id
|
||||
task.action_id,
|
||||
task.package_id
|
||||
),
|
||||
ErrorKind::InvalidRequest,
|
||||
));
|
||||
@@ -261,6 +261,9 @@ async fn request_action(
|
||||
},
|
||||
None => true,
|
||||
};
|
||||
if active && task.severity == TaskSeverity::Critical {
|
||||
context.stop(procedure_id).await?;
|
||||
}
|
||||
context
|
||||
.seed
|
||||
.ctx
|
||||
@@ -270,8 +273,8 @@ async fn request_action(
|
||||
.as_package_data_mut()
|
||||
.as_idx_mut(src_id)
|
||||
.or_not_found(src_id)?
|
||||
.as_requested_actions_mut()
|
||||
.insert(&replay_id, &ActionRequestEntry { active, request })
|
||||
.as_tasks_mut()
|
||||
.insert(&replay_id, &TaskEntry { active, task })
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
@@ -281,16 +284,16 @@ async fn request_action(
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS, Parser)]
|
||||
#[ts(type = "{ only: string[] } | { except: string[] }")]
|
||||
#[ts(export)]
|
||||
pub struct ClearActionRequestsParams {
|
||||
pub struct ClearTasksParams {
|
||||
#[arg(long, conflicts_with = "except")]
|
||||
pub only: Option<Vec<ReplayId>>,
|
||||
#[arg(long, conflicts_with = "only")]
|
||||
pub except: Option<Vec<ReplayId>>,
|
||||
}
|
||||
|
||||
async fn clear_action_requests(
|
||||
async fn clear_tasks(
|
||||
context: EffectContext,
|
||||
ClearActionRequestsParams { only, except }: ClearActionRequestsParams,
|
||||
ClearTasksParams { only, except }: ClearTasksParams,
|
||||
) -> Result<(), Error> {
|
||||
let context = context.deref()?;
|
||||
let package_id = context.seed.id.clone();
|
||||
@@ -305,7 +308,7 @@ async fn clear_action_requests(
|
||||
.as_package_data_mut()
|
||||
.as_idx_mut(&package_id)
|
||||
.or_not_found(&package_id)?
|
||||
.as_requested_actions_mut()
|
||||
.as_tasks_mut()
|
||||
.mutate(|a| {
|
||||
Ok(a.retain(|e, _| {
|
||||
only.as_ref().map_or(true, |only| !only.contains(e))
|
||||
|
||||
@@ -10,7 +10,7 @@ use models::{FromStrParser, HealthCheckId, PackageId, ReplayId, VersionString, V
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::db::model::package::{
|
||||
ActionRequestEntry, CurrentDependencies, CurrentDependencyInfo, CurrentDependencyKind,
|
||||
TaskEntry, CurrentDependencies, CurrentDependencyInfo, CurrentDependencyKind,
|
||||
ManifestPreference,
|
||||
};
|
||||
use crate::disk::mount::filesystem::bind::Bind;
|
||||
@@ -335,7 +335,7 @@ pub struct CheckDependenciesResult {
|
||||
installed_version: Option<VersionString>,
|
||||
satisfies: BTreeSet<VersionString>,
|
||||
is_running: bool,
|
||||
requested_actions: BTreeMap<ReplayId, ActionRequestEntry>,
|
||||
tasks: BTreeMap<ReplayId, TaskEntry>,
|
||||
#[ts(as = "BTreeMap::<HealthCheckId, NamedHealthCheckResult>")]
|
||||
health_checks: OrdMap<HealthCheckId, NamedHealthCheckResult>,
|
||||
}
|
||||
@@ -351,7 +351,7 @@ pub async fn check_dependencies(
|
||||
.as_idx(&context.seed.id)
|
||||
.or_not_found(&context.seed.id)?;
|
||||
let current_dependencies = pde.as_current_dependencies().de()?;
|
||||
let requested_actions = pde.as_requested_actions().de()?;
|
||||
let tasks = pde.as_tasks().de()?;
|
||||
let package_dependency_info: Vec<_> = package_ids
|
||||
.unwrap_or_else(|| current_dependencies.0.keys().cloned().collect())
|
||||
.into_iter()
|
||||
@@ -365,9 +365,9 @@ pub async fn check_dependencies(
|
||||
for (package_id, dependency_info) in package_dependency_info {
|
||||
let title = dependency_info.title.clone();
|
||||
let Some(package) = db.as_public().as_package_data().as_idx(&package_id) else {
|
||||
let requested_actions = requested_actions
|
||||
let tasks = tasks
|
||||
.iter()
|
||||
.filter(|(_, v)| v.request.package_id == package_id)
|
||||
.filter(|(_, v)| v.task.package_id == package_id)
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect();
|
||||
results.push(CheckDependenciesResult {
|
||||
@@ -376,7 +376,7 @@ pub async fn check_dependencies(
|
||||
installed_version: None,
|
||||
satisfies: BTreeSet::new(),
|
||||
is_running: false,
|
||||
requested_actions,
|
||||
tasks,
|
||||
health_checks: Default::default(),
|
||||
});
|
||||
continue;
|
||||
@@ -393,9 +393,9 @@ pub async fn check_dependencies(
|
||||
false
|
||||
};
|
||||
let health_checks = status.health().cloned().unwrap_or_default();
|
||||
let requested_actions = requested_actions
|
||||
let tasks = tasks
|
||||
.iter()
|
||||
.filter(|(_, v)| v.request.package_id == package_id)
|
||||
.filter(|(_, v)| v.task.package_id == package_id)
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect();
|
||||
results.push(CheckDependenciesResult {
|
||||
@@ -404,7 +404,7 @@ pub async fn check_dependencies(
|
||||
installed_version,
|
||||
satisfies,
|
||||
is_running,
|
||||
requested_actions,
|
||||
tasks,
|
||||
health_checks,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use models::VersionString;
|
||||
|
||||
use crate::service::effects::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS, Parser)]
|
||||
@@ -7,7 +5,7 @@ use crate::service::effects::prelude::*;
|
||||
#[ts(export)]
|
||||
pub struct SetDataVersionParams {
|
||||
#[ts(type = "string")]
|
||||
version: VersionString,
|
||||
version: Option<String>,
|
||||
}
|
||||
pub async fn set_data_version(
|
||||
context: EffectContext,
|
||||
@@ -25,7 +23,7 @@ pub async fn set_data_version(
|
||||
.as_idx_mut(package_id)
|
||||
.or_not_found(package_id)?
|
||||
.as_data_version_mut()
|
||||
.ser(&Some(version))
|
||||
.ser(&version)
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
@@ -33,7 +31,7 @@ pub async fn set_data_version(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_data_version(context: EffectContext) -> Result<Option<VersionString>, Error> {
|
||||
pub async fn get_data_version(context: EffectContext) -> Result<Option<String>, Error> {
|
||||
let context = context.deref()?;
|
||||
let package_id = &context.seed.id;
|
||||
context
|
||||
|
||||
@@ -17,7 +17,7 @@ use futures::{FutureExt, SinkExt, StreamExt, TryStreamExt};
|
||||
use helpers::NonDetachingJoinHandle;
|
||||
use imbl_value::{json, InternedString};
|
||||
use itertools::Itertools;
|
||||
use models::{ActionId, HostId, ImageId, PackageId, ProcedureName};
|
||||
use models::{ActionId, HostId, ImageId, PackageId};
|
||||
use nix::sys::signal::Signal;
|
||||
use persistent_container::{PersistentContainer, Subcontainer};
|
||||
use rpc_toolkit::{from_fn_async, CallRemoteHandler, Empty, HandlerArgs, HandlerFor};
|
||||
@@ -30,27 +30,31 @@ use tokio::process::Command;
|
||||
use tokio::sync::Notify;
|
||||
use tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode;
|
||||
use ts_rs::TS;
|
||||
use url::Url;
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::package::{
|
||||
InstalledState, PackageDataEntry, PackageState, PackageStateMatchModelRef, UpdatingState,
|
||||
InstalledState, ManifestPreference, PackageDataEntry, PackageState, PackageStateMatchModelRef,
|
||||
UpdatingState,
|
||||
};
|
||||
use crate::disk::mount::guard::GenericMountGuard;
|
||||
use crate::disk::mount::filesystem::ReadOnly;
|
||||
use crate::disk::mount::guard::{GenericMountGuard, MountGuard};
|
||||
use crate::install::PKG_ARCHIVE_DIR;
|
||||
use crate::lxc::ContainerId;
|
||||
use crate::prelude::*;
|
||||
use crate::progress::{NamedProgress, Progress};
|
||||
use crate::rpc_continuations::{Guid, RpcContinuation};
|
||||
use crate::s9pk::S9pk;
|
||||
use crate::service::action::update_requested_actions;
|
||||
use crate::service::action::update_tasks;
|
||||
use crate::service::rpc::{ExitParams, InitKind};
|
||||
use crate::service::service_map::InstallProgressHandles;
|
||||
use crate::util::actor::concurrent::ConcurrentActor;
|
||||
use crate::util::io::{create_file, AsyncReadStream, TermSize};
|
||||
use crate::util::net::WebSocketExt;
|
||||
use crate::util::serde::{NoOutput, Pem};
|
||||
use crate::util::serde::Pem;
|
||||
use crate::util::Never;
|
||||
use crate::volume::data_dir;
|
||||
use crate::{CAP_1_KiB, DATA_DIR, PACKAGE_DATA};
|
||||
use crate::{CAP_1_KiB, DATA_DIR};
|
||||
|
||||
pub mod action;
|
||||
pub mod cli;
|
||||
@@ -62,6 +66,7 @@ mod service_actor;
|
||||
pub mod service_map;
|
||||
pub mod start_stop;
|
||||
mod transition;
|
||||
pub mod uninstall;
|
||||
mod util;
|
||||
|
||||
pub use service_map::ServiceMap;
|
||||
@@ -116,99 +121,35 @@ impl ServiceRef {
|
||||
pub fn weak(&self) -> Weak<Service> {
|
||||
Arc::downgrade(&self.0)
|
||||
}
|
||||
pub async fn uninstall(
|
||||
self,
|
||||
target_version: Option<models::VersionString>,
|
||||
soft: bool,
|
||||
force: bool,
|
||||
) -> Result<(), Error> {
|
||||
let uninit_res = self
|
||||
.seed
|
||||
.persistent_container
|
||||
.execute::<NoOutput>(
|
||||
Guid::new(),
|
||||
ProcedureName::PackageUninit,
|
||||
to_value(&target_version)?,
|
||||
None,
|
||||
) // TODO timeout
|
||||
.await;
|
||||
pub async fn uninstall(self, uninit: ExitParams, soft: bool, force: bool) -> Result<(), Error> {
|
||||
let id = self.seed.persistent_container.s9pk.as_manifest().id.clone();
|
||||
let ctx = self.seed.ctx.clone();
|
||||
let uninit_res = self.shutdown(Some(uninit.clone())).await;
|
||||
if force {
|
||||
uninit_res.log_err();
|
||||
} else {
|
||||
uninit_res?;
|
||||
}
|
||||
let id = self.seed.persistent_container.s9pk.as_manifest().id.clone();
|
||||
let ctx = self.seed.ctx.clone();
|
||||
self.shutdown().await?;
|
||||
|
||||
if target_version.is_none() {
|
||||
if let Some(pde) = ctx
|
||||
.db
|
||||
.mutate(|d| {
|
||||
if let Some(pde) = d
|
||||
.as_public_mut()
|
||||
.as_package_data_mut()
|
||||
.remove(&id)?
|
||||
.map(|d| d.de())
|
||||
.transpose()?
|
||||
{
|
||||
d.as_private_mut().as_available_ports_mut().mutate(|p| {
|
||||
p.free(
|
||||
pde.hosts
|
||||
.0
|
||||
.values()
|
||||
.flat_map(|h| h.bindings.values())
|
||||
.flat_map(|b| {
|
||||
b.net
|
||||
.assigned_port
|
||||
.into_iter()
|
||||
.chain(b.net.assigned_ssl_port)
|
||||
}),
|
||||
);
|
||||
Ok(())
|
||||
})?;
|
||||
d.as_private_mut().as_package_stores_mut().remove(&id)?;
|
||||
Ok(Some(pde))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})
|
||||
.await
|
||||
.result?
|
||||
{
|
||||
let state = pde.state_info.expect_removing()?;
|
||||
if !soft {
|
||||
for volume_id in &state.manifest.volumes {
|
||||
let path = data_dir(DATA_DIR, &state.manifest.id, volume_id);
|
||||
if tokio::fs::metadata(&path).await.is_ok() {
|
||||
tokio::fs::remove_dir_all(&path).await?;
|
||||
}
|
||||
}
|
||||
let logs_dir = Path::new(PACKAGE_DATA)
|
||||
.join("logs")
|
||||
.join(&state.manifest.id);
|
||||
if tokio::fs::metadata(&logs_dir).await.is_ok() {
|
||||
tokio::fs::remove_dir_all(&logs_dir).await?;
|
||||
}
|
||||
let archive_path = Path::new(PACKAGE_DATA)
|
||||
.join("archive")
|
||||
.join("installed")
|
||||
.join(&state.manifest.id);
|
||||
if tokio::fs::metadata(&archive_path).await.is_ok() {
|
||||
tokio::fs::remove_file(&archive_path).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
if uninit.is_uninstall() {
|
||||
uninstall::cleanup(&ctx, &id, soft).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub async fn shutdown(self) -> Result<(), Error> {
|
||||
pub async fn shutdown(self, uninit: Option<ExitParams>) -> Result<(), Error> {
|
||||
if let Some((hdl, shutdown)) = self.seed.persistent_container.rpc_server.send_replace(None)
|
||||
{
|
||||
self.seed
|
||||
.persistent_container
|
||||
.rpc_client
|
||||
.request(rpc::Exit, Empty {})
|
||||
.request(
|
||||
rpc::Exit,
|
||||
uninit.clone().unwrap_or_else(|| {
|
||||
ExitParams::target_version(
|
||||
&*self.seed.persistent_container.s9pk.as_manifest().version,
|
||||
)
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
shutdown.shutdown();
|
||||
tokio::time::timeout(Duration::from_secs(30), hdl)
|
||||
@@ -234,11 +175,12 @@ impl ServiceRef {
|
||||
)
|
||||
})?
|
||||
.persistent_container
|
||||
.exit()
|
||||
.exit(uninit)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ServiceRef {
|
||||
type Target = Service;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@@ -257,7 +199,14 @@ pub struct Service {
|
||||
}
|
||||
impl Service {
|
||||
#[instrument(skip_all)]
|
||||
async fn new(ctx: RpcContext, s9pk: S9pk, start: StartStop) -> Result<ServiceRef, Error> {
|
||||
async fn new(
|
||||
ctx: RpcContext,
|
||||
s9pk: S9pk,
|
||||
start: StartStop,
|
||||
procedure_id: Guid,
|
||||
init_kind: Option<InitKind>,
|
||||
recovery_source: Option<impl GenericMountGuard>,
|
||||
) -> Result<ServiceRef, Error> {
|
||||
let id = s9pk.as_manifest().id.clone();
|
||||
let persistent_container = PersistentContainer::new(
|
||||
&ctx, s9pk,
|
||||
@@ -277,11 +226,28 @@ impl Service {
|
||||
seed,
|
||||
}
|
||||
.into();
|
||||
let recovery_guard = if let Some(recovery_source) = &recovery_source {
|
||||
Some(
|
||||
service
|
||||
.seed
|
||||
.persistent_container
|
||||
.mount_backup(recovery_source.path().join("data"), ReadOnly)
|
||||
.await?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
service
|
||||
.seed
|
||||
.persistent_container
|
||||
.init(service.weak())
|
||||
.init(service.weak(), procedure_id, init_kind)
|
||||
.await?;
|
||||
if let Some(recovery_guard) = recovery_guard {
|
||||
recovery_guard.unmount(true).await?;
|
||||
}
|
||||
if let Some(recovery_source) = recovery_source {
|
||||
recovery_source.unmount().await?;
|
||||
}
|
||||
Ok(service)
|
||||
}
|
||||
|
||||
@@ -305,7 +271,9 @@ impl Service {
|
||||
} else {
|
||||
StartStop::Stop
|
||||
};
|
||||
Self::new(ctx, s9pk, start_stop).await.map(Some)
|
||||
Self::new(ctx, s9pk, start_stop, Guid::new(), None, None::<MountGuard>)
|
||||
.await
|
||||
.map(Some)
|
||||
}
|
||||
};
|
||||
let s9pk_dir = Path::new(DATA_DIR).join(PKG_ARCHIVE_DIR).join("installed"); // TODO: make this based on hash
|
||||
@@ -328,7 +296,7 @@ impl Service {
|
||||
tracing::debug!("{e:?}")
|
||||
}) {
|
||||
if let Ok(service) =
|
||||
Self::install(ctx.clone(), s9pk, None, None::<Never>, None)
|
||||
Self::install(ctx.clone(), s9pk, &None, None, None::<Never>, None)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("Error installing service: {e}");
|
||||
@@ -365,7 +333,8 @@ impl Service {
|
||||
if let Ok(service) = Self::install(
|
||||
ctx.clone(),
|
||||
s9pk,
|
||||
Some(s.as_manifest().as_version().de()?),
|
||||
&None,
|
||||
Some(entry.as_status().de()?.run_state()),
|
||||
None::<Never>,
|
||||
None,
|
||||
)
|
||||
@@ -407,27 +376,66 @@ impl Service {
|
||||
tracing::error!("Error opening s9pk for removal: {e}");
|
||||
tracing::debug!("{e:?}")
|
||||
}) {
|
||||
if let Ok(service) = Self::new(ctx.clone(), s9pk, StartStop::Stop)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("Error loading service for removal: {e}");
|
||||
tracing::debug!("{e:?}")
|
||||
})
|
||||
let err_state = |e: Error| async move {
|
||||
let state = crate::status::MainStatus::Error {
|
||||
on_rebuild: StartStop::Stop,
|
||||
message: e.to_string(),
|
||||
debug: Some(format!("{e:?}")),
|
||||
};
|
||||
ctx.db
|
||||
.mutate(move |db| {
|
||||
if let Some(pde) =
|
||||
db.as_public_mut().as_package_data_mut().as_idx_mut(&id)
|
||||
{
|
||||
pde.as_state_info_mut().map_mutate(|s| {
|
||||
Ok(PackageState::Installed(InstalledState {
|
||||
manifest: s
|
||||
.as_manifest(ManifestPreference::Old)
|
||||
.clone(),
|
||||
}))
|
||||
})?;
|
||||
pde.as_status_mut().ser(&state)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
.result
|
||||
};
|
||||
match Self::new(
|
||||
ctx.clone(),
|
||||
s9pk,
|
||||
StartStop::Stop,
|
||||
Guid::new(),
|
||||
None,
|
||||
None::<MountGuard>,
|
||||
)
|
||||
.await
|
||||
{
|
||||
match service.uninstall(None, false, false).await {
|
||||
Ok(service) => match service
|
||||
.uninstall(ExitParams::uninstall(), false, false)
|
||||
.await
|
||||
{
|
||||
Err(e) => {
|
||||
tracing::error!("Error uninstalling service: {e}");
|
||||
tracing::debug!("{e:?}")
|
||||
tracing::debug!("{e:?}");
|
||||
err_state(e).await?;
|
||||
}
|
||||
Ok(()) => return Ok(None),
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::error!("Error loading service for removal: {e}");
|
||||
tracing::debug!("{e:?}");
|
||||
err_state(e).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.db
|
||||
.mutate(|v| v.as_public_mut().as_package_data_mut().remove(id))
|
||||
.await
|
||||
.result?;
|
||||
if disposition == LoadDisposition::Retry {
|
||||
ctx.db
|
||||
.mutate(|v| v.as_public_mut().as_package_data_mut().remove(id))
|
||||
.await
|
||||
.result?;
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
@@ -445,53 +453,30 @@ impl Service {
|
||||
pub async fn install(
|
||||
ctx: RpcContext,
|
||||
s9pk: S9pk,
|
||||
mut src_version: Option<models::VersionString>,
|
||||
registry: &Option<Url>,
|
||||
prev_state: Option<StartStop>,
|
||||
recovery_source: Option<impl GenericMountGuard>,
|
||||
progress: Option<InstallProgressHandles>,
|
||||
) -> Result<ServiceRef, Error> {
|
||||
let manifest = s9pk.as_manifest().clone();
|
||||
let developer_key = s9pk.as_archive().signer();
|
||||
let icon = s9pk.icon_data_url().await?;
|
||||
let service = Self::new(ctx.clone(), s9pk, StartStop::Stop).await?;
|
||||
|
||||
if let Some(recovery_source) = recovery_source {
|
||||
service
|
||||
.actor
|
||||
.send(
|
||||
Guid::new(),
|
||||
transition::restore::Restore {
|
||||
path: recovery_source.path().to_path_buf(),
|
||||
},
|
||||
)
|
||||
.await??;
|
||||
recovery_source.unmount().await?;
|
||||
src_version = Some(
|
||||
service
|
||||
.seed
|
||||
.persistent_container
|
||||
.s9pk
|
||||
.as_manifest()
|
||||
.version
|
||||
.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
let procedure_id = Guid::new();
|
||||
service
|
||||
.seed
|
||||
.persistent_container
|
||||
.execute::<NoOutput>(
|
||||
procedure_id.clone(),
|
||||
ProcedureName::PackageInit,
|
||||
to_value(&src_version)?,
|
||||
None,
|
||||
) // TODO timeout
|
||||
.await
|
||||
.with_kind(if src_version.is_some() {
|
||||
ErrorKind::UpdateFailed
|
||||
let service = Self::new(
|
||||
ctx.clone(),
|
||||
s9pk,
|
||||
StartStop::Stop,
|
||||
procedure_id.clone(),
|
||||
Some(if recovery_source.is_some() {
|
||||
InitKind::Restore
|
||||
} else if prev_state.is_some() {
|
||||
InitKind::Update
|
||||
} else {
|
||||
ErrorKind::InstallFailed
|
||||
})?; // TODO: handle cancellation
|
||||
InitKind::Install
|
||||
}),
|
||||
recovery_source,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(mut progress) = progress {
|
||||
progress.finalization_progress.complete();
|
||||
@@ -501,19 +486,19 @@ impl Service {
|
||||
|
||||
let peek = ctx.db.peek().await;
|
||||
let mut action_input: BTreeMap<ActionId, Value> = BTreeMap::new();
|
||||
let requested_actions: BTreeSet<_> = peek
|
||||
let tasks: BTreeSet<_> = peek
|
||||
.as_public()
|
||||
.as_package_data()
|
||||
.as_entries()?
|
||||
.into_iter()
|
||||
.map(|(_, pde)| {
|
||||
Ok(pde
|
||||
.as_requested_actions()
|
||||
.as_tasks()
|
||||
.as_entries()?
|
||||
.into_iter()
|
||||
.map(|(_, r)| {
|
||||
Ok::<_, Error>(if r.as_request().as_package_id().de()? == manifest.id {
|
||||
Some(r.as_request().as_action_id().de()?)
|
||||
Ok::<_, Error>(if r.as_task().as_package_id().de()? == manifest.id {
|
||||
Some(r.as_task().as_action_id().de()?)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
@@ -523,27 +508,30 @@ impl Service {
|
||||
.flatten_ok()
|
||||
.map(|a| a.and_then(|a| a))
|
||||
.try_collect()?;
|
||||
for action_id in requested_actions {
|
||||
if let Some(input) = service
|
||||
.get_action_input(procedure_id.clone(), action_id.clone())
|
||||
.await?
|
||||
.and_then(|i| i.value)
|
||||
for action_id in tasks {
|
||||
if peek
|
||||
.as_public()
|
||||
.as_package_data()
|
||||
.as_idx(&manifest.id)
|
||||
.or_not_found(&manifest.id)?
|
||||
.as_actions()
|
||||
.contains_key(&action_id)?
|
||||
{
|
||||
action_input.insert(action_id, input);
|
||||
if let Some(input) = service
|
||||
.get_action_input(procedure_id.clone(), action_id.clone())
|
||||
.await?
|
||||
.and_then(|i| i.value)
|
||||
{
|
||||
action_input.insert(action_id, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
for (action_id, input) in &action_input {
|
||||
for (_, pde) in db.as_public_mut().as_package_data_mut().as_entries_mut()? {
|
||||
pde.as_requested_actions_mut().mutate(|requested_actions| {
|
||||
Ok(update_requested_actions(
|
||||
requested_actions,
|
||||
&manifest.id,
|
||||
action_id,
|
||||
input,
|
||||
false,
|
||||
))
|
||||
pde.as_tasks_mut().mutate(|tasks| {
|
||||
Ok(update_tasks(tasks, &manifest.id, action_id, input, false))
|
||||
})?;
|
||||
}
|
||||
}
|
||||
@@ -552,13 +540,18 @@ impl Service {
|
||||
.as_package_data_mut()
|
||||
.as_idx_mut(&manifest.id)
|
||||
.or_not_found(&manifest.id)?;
|
||||
let actions = entry.as_actions().keys()?;
|
||||
entry.as_tasks_mut().mutate(|t| {
|
||||
Ok(t.retain(|_, v| {
|
||||
v.task.package_id != manifest.id || actions.contains(&v.task.action_id)
|
||||
}))
|
||||
})?;
|
||||
entry
|
||||
.as_state_info_mut()
|
||||
.ser(&PackageState::Installed(InstalledState { manifest }))?;
|
||||
entry.as_developer_key_mut().ser(&Pem::new(developer_key))?;
|
||||
entry.as_icon_mut().ser(&icon)?;
|
||||
// TODO: marketplace url
|
||||
// TODO: dependency info
|
||||
entry.as_registry_mut().ser(registry)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
@@ -583,7 +576,7 @@ impl Service {
|
||||
.send(
|
||||
Guid::new(),
|
||||
transition::backup::Backup {
|
||||
path: guard.path().to_path_buf(),
|
||||
path: guard.path().join("data"),
|
||||
},
|
||||
)
|
||||
.await??
|
||||
|
||||
@@ -31,7 +31,9 @@ use crate::s9pk::merkle_archive::source::FileSource;
|
||||
use crate::s9pk::S9pk;
|
||||
use crate::service::effects::context::EffectContext;
|
||||
use crate::service::effects::handler;
|
||||
use crate::service::rpc::{CallbackHandle, CallbackId, CallbackParams};
|
||||
use crate::service::rpc::{
|
||||
CallbackHandle, CallbackId, CallbackParams, ExitParams, InitKind, InitParams,
|
||||
};
|
||||
use crate::service::start_stop::StartStop;
|
||||
use crate::service::transition::{TransitionKind, TransitionState};
|
||||
use crate::service::{rpc, RunningStatus, Service};
|
||||
@@ -369,7 +371,12 @@ impl PersistentContainer {
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn init(&self, seed: Weak<Service>) -> Result<(), Error> {
|
||||
pub async fn init(
|
||||
&self,
|
||||
seed: Weak<Service>,
|
||||
procedure_id: Guid,
|
||||
kind: Option<InitKind>,
|
||||
) -> Result<(), Error> {
|
||||
let socket_server_context = EffectContext::new(seed);
|
||||
let server = Server::new(move || ready(Ok(socket_server_context.clone())), handler());
|
||||
let path = self
|
||||
@@ -424,7 +431,15 @@ impl PersistentContainer {
|
||||
));
|
||||
}
|
||||
|
||||
self.rpc_client.request(rpc::Init, Empty {}).await?;
|
||||
self.rpc_client
|
||||
.request(
|
||||
rpc::Init,
|
||||
InitParams {
|
||||
id: procedure_id,
|
||||
kind,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.state.send_modify(|s| s.rt_initialized = true);
|
||||
|
||||
@@ -435,10 +450,12 @@ impl PersistentContainer {
|
||||
fn destroy(
|
||||
&mut self,
|
||||
error: bool,
|
||||
uninit: Option<ExitParams>,
|
||||
) -> Option<impl Future<Output = Result<(), Error>> + 'static> {
|
||||
if self.destroyed {
|
||||
return None;
|
||||
}
|
||||
let version = self.s9pk.as_manifest().version.clone();
|
||||
let rpc_client = self.rpc_client.clone();
|
||||
let rpc_server = self.rpc_server.send_replace(None);
|
||||
let js_mount = self.js_mount.take();
|
||||
@@ -469,7 +486,14 @@ impl PersistentContainer {
|
||||
}
|
||||
}
|
||||
if let Some((hdl, shutdown)) = rpc_server {
|
||||
errs.handle(rpc_client.request(rpc::Exit, Empty {}).await);
|
||||
errs.handle(
|
||||
rpc_client
|
||||
.request(
|
||||
rpc::Exit,
|
||||
uninit.unwrap_or_else(|| ExitParams::target_version(&*version)),
|
||||
)
|
||||
.await,
|
||||
);
|
||||
shutdown.shutdown();
|
||||
errs.handle(hdl.await.with_kind(ErrorKind::Cancelled));
|
||||
}
|
||||
@@ -494,8 +518,8 @@ impl PersistentContainer {
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn exit(mut self) -> Result<(), Error> {
|
||||
if let Some(destroy) = self.destroy(false) {
|
||||
pub async fn exit(mut self, uninit: Option<ExitParams>) -> Result<(), Error> {
|
||||
if let Some(destroy) = self.destroy(false, uninit) {
|
||||
destroy.await?;
|
||||
}
|
||||
tracing::info!("Service for {} exited", self.s9pk.as_manifest().id);
|
||||
@@ -613,7 +637,7 @@ impl PersistentContainer {
|
||||
|
||||
impl Drop for PersistentContainer {
|
||||
fn drop(&mut self) {
|
||||
if let Some(destroy) = self.destroy(true) {
|
||||
if let Some(destroy) = self.destroy(true, None) {
|
||||
tokio::spawn(async move { destroy.await.log_err() });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ use std::sync::{Arc, Weak};
|
||||
use std::time::Duration;
|
||||
|
||||
use clap::builder::ValueParserFactory;
|
||||
use exver::{ExtendedVersion, VersionRange};
|
||||
use imbl::Vector;
|
||||
use imbl_value::Value;
|
||||
use imbl_value::{InternedString, Value};
|
||||
use models::{FromStrParser, ProcedureName};
|
||||
use rpc_toolkit::yajrc::RpcMethod;
|
||||
use rpc_toolkit::Empty;
|
||||
@@ -16,10 +17,25 @@ use crate::rpc_continuations::Guid;
|
||||
use crate::service::persistent_container::PersistentContainer;
|
||||
use crate::util::Never;
|
||||
|
||||
#[derive(Clone, serde::Deserialize, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum InitKind {
|
||||
Install,
|
||||
Update,
|
||||
Restore,
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Deserialize, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InitParams {
|
||||
pub id: Guid,
|
||||
pub kind: Option<InitKind>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Init;
|
||||
impl RpcMethod for Init {
|
||||
type Params = Empty;
|
||||
type Params = InitParams;
|
||||
type Response = ();
|
||||
fn as_str<'a>(&'a self) -> &'a str {
|
||||
"init"
|
||||
@@ -70,10 +86,42 @@ impl serde::Serialize for Stop {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Deserialize, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ExitParams {
|
||||
id: Guid,
|
||||
/// VersionRange or ExtendedVersion
|
||||
#[ts(type = "string | null")]
|
||||
target: Option<InternedString>,
|
||||
}
|
||||
impl ExitParams {
|
||||
pub fn target_version(version: &ExtendedVersion) -> Self {
|
||||
Self {
|
||||
id: Guid::new(),
|
||||
target: Some(InternedString::from_display(version)),
|
||||
}
|
||||
}
|
||||
pub fn target_range(range: &VersionRange) -> Self {
|
||||
Self {
|
||||
id: Guid::new(),
|
||||
target: Some(InternedString::from_display(range)),
|
||||
}
|
||||
}
|
||||
pub fn uninstall() -> Self {
|
||||
Self {
|
||||
id: Guid::new(),
|
||||
target: None,
|
||||
}
|
||||
}
|
||||
pub fn is_uninstall(&self) -> bool {
|
||||
self.target.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Exit;
|
||||
impl RpcMethod for Exit {
|
||||
type Params = Empty;
|
||||
type Params = ExitParams;
|
||||
type Response = ();
|
||||
fn as_str<'a>(&'a self) -> &'a str {
|
||||
"exit"
|
||||
|
||||
@@ -58,10 +58,6 @@ async fn service_actor_loop(
|
||||
transition_state: Some(TransitionKind::Restarting),
|
||||
..
|
||||
} => MainStatus::Restarting,
|
||||
ServiceStateKinds {
|
||||
transition_state: Some(TransitionKind::Restoring),
|
||||
..
|
||||
} => MainStatus::Restoring,
|
||||
ServiceStateKinds {
|
||||
transition_state: Some(TransitionKind::BackingUp),
|
||||
..
|
||||
|
||||
@@ -3,15 +3,16 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use exver::VersionRange;
|
||||
use futures::future::{BoxFuture, Fuse};
|
||||
use futures::stream::FuturesUnordered;
|
||||
use futures::{Future, FutureExt, StreamExt, TryFutureExt};
|
||||
use helpers::NonDetachingJoinHandle;
|
||||
use imbl::OrdMap;
|
||||
use imbl_value::InternedString;
|
||||
use models::ErrorData;
|
||||
use tokio::sync::{oneshot, Mutex, OwnedRwLockReadGuard, OwnedRwLockWriteGuard, RwLock};
|
||||
use tracing::instrument;
|
||||
use url::Url;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
use crate::db::model::package::{
|
||||
@@ -22,9 +23,11 @@ use crate::install::PKG_ARCHIVE_DIR;
|
||||
use crate::notifications::{notify, NotificationLevel};
|
||||
use crate::prelude::*;
|
||||
use crate::progress::{FullProgressTracker, PhaseProgressTrackerHandle, ProgressTrackerWriter};
|
||||
use crate::rpc_continuations::Guid;
|
||||
use crate::s9pk::manifest::PackageId;
|
||||
use crate::s9pk::merkle_archive::source::FileSource;
|
||||
use crate::s9pk::S9pk;
|
||||
use crate::service::rpc::ExitParams;
|
||||
use crate::service::start_stop::StartStop;
|
||||
use crate::service::{LoadDisposition, Service, ServiceRef};
|
||||
use crate::status::MainStatus;
|
||||
@@ -94,7 +97,7 @@ impl ServiceMap {
|
||||
let mut shutdown_err = Ok(());
|
||||
let mut service = self.get_mut(id).await;
|
||||
if let Some(service) = service.take() {
|
||||
shutdown_err = service.shutdown().await;
|
||||
shutdown_err = service.shutdown(None).await;
|
||||
}
|
||||
match Service::load(ctx, id, disposition).await {
|
||||
Ok(s) => *service = s.into(),
|
||||
@@ -130,6 +133,7 @@ impl ServiceMap {
|
||||
&self,
|
||||
ctx: RpcContext,
|
||||
s9pk: F,
|
||||
registry: Option<Url>,
|
||||
recovery_source: Option<impl GenericMountGuard>,
|
||||
progress: Option<FullProgressTracker>,
|
||||
) -> Result<DownloadInstallFuture, Error>
|
||||
@@ -181,6 +185,7 @@ impl ServiceMap {
|
||||
let manifest = manifest.clone();
|
||||
let id = id.clone();
|
||||
let install_progress = progress.snapshot();
|
||||
let registry = registry.clone();
|
||||
move |db| {
|
||||
if let Some(pde) =
|
||||
db.as_public_mut().as_package_data_mut().as_idx_mut(&id)
|
||||
@@ -212,13 +217,13 @@ impl ServiceMap {
|
||||
},
|
||||
data_version: None,
|
||||
status: MainStatus::Stopped,
|
||||
registry: None,
|
||||
registry,
|
||||
developer_key: Pem::new(developer_key),
|
||||
icon,
|
||||
last_backup: None,
|
||||
current_dependencies: Default::default(),
|
||||
actions: Default::default(),
|
||||
requested_actions: Default::default(),
|
||||
tasks: Default::default(),
|
||||
service_interfaces: Default::default(),
|
||||
hosts: Default::default(),
|
||||
store_exposed_dependents: Default::default(),
|
||||
@@ -287,35 +292,59 @@ impl ServiceMap {
|
||||
ErrorKind::InvalidRequest,
|
||||
"cannot restore over existing package"
|
||||
);
|
||||
let version = service
|
||||
let prev_version = service
|
||||
.seed
|
||||
.persistent_container
|
||||
.s9pk
|
||||
.as_manifest()
|
||||
.version
|
||||
.clone();
|
||||
service
|
||||
.uninstall(Some(s9pk.as_manifest().version.clone()), false, false)
|
||||
.await?;
|
||||
let prev_can_migrate_to = &service
|
||||
.seed
|
||||
.persistent_container
|
||||
.s9pk
|
||||
.as_manifest()
|
||||
.can_migrate_to;
|
||||
let next_version = &s9pk.as_manifest().version;
|
||||
let next_can_migrate_from = &s9pk.as_manifest().can_migrate_from;
|
||||
let uninit = if prev_version.satisfies(next_can_migrate_from) {
|
||||
ExitParams::target_version(&*prev_version)
|
||||
} else if next_version.satisfies(prev_can_migrate_to) {
|
||||
ExitParams::target_version(&s9pk.as_manifest().version)
|
||||
} else {
|
||||
ExitParams::target_range(&VersionRange::and(
|
||||
prev_can_migrate_to.clone(),
|
||||
next_can_migrate_from.clone(),
|
||||
))
|
||||
};
|
||||
let run_state = service
|
||||
.seed
|
||||
.persistent_container
|
||||
.state
|
||||
.borrow()
|
||||
.desired_state;
|
||||
service.uninstall(uninit, false, false).await?;
|
||||
progress.complete();
|
||||
Some(version)
|
||||
Some(run_state)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
*service = Some(
|
||||
Service::install(
|
||||
ctx,
|
||||
s9pk,
|
||||
prev,
|
||||
recovery_source,
|
||||
Some(InstallProgressHandles {
|
||||
finalization_progress,
|
||||
progress,
|
||||
}),
|
||||
)
|
||||
.await?
|
||||
.into(),
|
||||
);
|
||||
let new_service = Service::install(
|
||||
ctx,
|
||||
s9pk,
|
||||
®istry,
|
||||
prev,
|
||||
recovery_source,
|
||||
Some(InstallProgressHandles {
|
||||
finalization_progress,
|
||||
progress,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
if prev == Some(StartStop::Start) {
|
||||
new_service.start(Guid::new()).await?;
|
||||
}
|
||||
*service = Some(new_service.into());
|
||||
drop(service);
|
||||
|
||||
sync_progress_task.await.map_err(|_| {
|
||||
@@ -359,14 +388,23 @@ impl ServiceMap {
|
||||
ServiceRefReloadCancelGuard::new(ctx.clone(), id.clone(), "Uninstall", None)
|
||||
.handle_last(async move {
|
||||
if let Some(service) = guard.take() {
|
||||
let res = service.uninstall(None, soft, force).await;
|
||||
let res = service
|
||||
.uninstall(ExitParams::uninstall(), soft, force)
|
||||
.await;
|
||||
drop(guard);
|
||||
res
|
||||
} else {
|
||||
Err(Error::new(
|
||||
eyre!("service {id} failed to initialize - cannot remove gracefully"),
|
||||
ErrorKind::Uninitialized,
|
||||
))
|
||||
if force {
|
||||
super::uninstall::cleanup(&ctx, &id, soft).await?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::new(
|
||||
eyre!(
|
||||
"service {id} failed to initialize - cannot remove gracefully"
|
||||
),
|
||||
ErrorKind::Uninitialized,
|
||||
))
|
||||
}
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
@@ -382,7 +420,7 @@ impl ServiceMap {
|
||||
for service in lock.values().cloned() {
|
||||
futs.push(async move {
|
||||
if let Some(service) = service.write_owned().await.take() {
|
||||
service.shutdown().await?
|
||||
service.shutdown(None).await?
|
||||
}
|
||||
Ok::<_, Error>(())
|
||||
});
|
||||
|
||||
@@ -10,13 +10,11 @@ use crate::util::future::{CancellationHandle, RemoteCancellable};
|
||||
|
||||
pub mod backup;
|
||||
pub mod restart;
|
||||
pub mod restore;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum TransitionKind {
|
||||
BackingUp,
|
||||
Restarting,
|
||||
Restoring,
|
||||
}
|
||||
|
||||
/// Used only in the manager/mod and is used to keep track of the state of the manager during the
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use futures::channel::oneshot;
|
||||
use futures::FutureExt;
|
||||
use models::ProcedureName;
|
||||
|
||||
use crate::disk::mount::filesystem::ReadOnly;
|
||||
use crate::prelude::*;
|
||||
use crate::rpc_continuations::Guid;
|
||||
use crate::service::transition::{TransitionKind, TransitionState};
|
||||
use crate::service::ServiceActor;
|
||||
use crate::util::actor::background::BackgroundJobQueue;
|
||||
use crate::util::actor::{ConflictBuilder, Handler};
|
||||
use crate::util::future::RemoteCancellable;
|
||||
use crate::util::serde::NoOutput;
|
||||
|
||||
pub(in crate::service) struct Restore {
|
||||
pub path: PathBuf,
|
||||
}
|
||||
impl Handler<Restore> for ServiceActor {
|
||||
type Response = Result<(), Error>;
|
||||
fn conflicts_with(_: &Restore) -> ConflictBuilder<Self> {
|
||||
ConflictBuilder::everything()
|
||||
}
|
||||
async fn handle(
|
||||
&mut self,
|
||||
id: Guid,
|
||||
restore: Restore,
|
||||
jobs: &BackgroundJobQueue,
|
||||
) -> Self::Response {
|
||||
// So Need a handle to just a single field in the state
|
||||
let path = restore.path;
|
||||
let seed = self.0.clone();
|
||||
|
||||
let state = self.0.persistent_container.state.clone();
|
||||
let (send_res, recv_res) = oneshot::channel();
|
||||
let transition = RemoteCancellable::new(
|
||||
async move {
|
||||
let backup_guard = seed
|
||||
.persistent_container
|
||||
.mount_backup(path, ReadOnly)
|
||||
.await?;
|
||||
seed.persistent_container
|
||||
.execute::<NoOutput>(id, ProcedureName::RestoreBackup, Value::Null, None)
|
||||
.await?;
|
||||
backup_guard.unmount(true).await?;
|
||||
|
||||
state.send_modify(|s| {
|
||||
s.transition_state.take();
|
||||
});
|
||||
Ok::<_, Error>(())
|
||||
}
|
||||
.map(|res| send_res.send(res)),
|
||||
);
|
||||
let cancel_handle = transition.cancellation_handle();
|
||||
jobs.add_job(transition.map(|_| ()));
|
||||
|
||||
let mut old = None;
|
||||
self.0.persistent_container.state.send_modify(|s| {
|
||||
old = std::mem::replace(
|
||||
&mut s.transition_state,
|
||||
Some(TransitionState {
|
||||
kind: TransitionKind::Restoring,
|
||||
cancel_handle,
|
||||
}),
|
||||
)
|
||||
});
|
||||
if let Some(t) = old {
|
||||
t.abort().await;
|
||||
}
|
||||
match recv_res.await {
|
||||
Err(_) => Err(Error::new(eyre!("Restoring canceled"), ErrorKind::Unknown)),
|
||||
Ok(res) => res,
|
||||
}
|
||||
}
|
||||
}
|
||||
70
core/startos/src/service/uninstall.rs
Normal file
70
core/startos/src/service/uninstall.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use std::path::Path;
|
||||
|
||||
use models::PackageId;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
use crate::prelude::*;
|
||||
use crate::volume::data_dir;
|
||||
use crate::{DATA_DIR, PACKAGE_DATA};
|
||||
|
||||
pub async fn cleanup(ctx: &RpcContext, id: &PackageId, soft: bool) -> Result<(), Error> {
|
||||
Ok(
|
||||
if let Some(pde) = ctx
|
||||
.db
|
||||
.mutate(|d| {
|
||||
if let Some(pde) = d
|
||||
.as_public_mut()
|
||||
.as_package_data_mut()
|
||||
.remove(&id)?
|
||||
.map(|d| d.de())
|
||||
.transpose()?
|
||||
{
|
||||
d.as_private_mut().as_available_ports_mut().mutate(|p| {
|
||||
p.free(
|
||||
pde.hosts
|
||||
.0
|
||||
.values()
|
||||
.flat_map(|h| h.bindings.values())
|
||||
.flat_map(|b| {
|
||||
b.net
|
||||
.assigned_port
|
||||
.into_iter()
|
||||
.chain(b.net.assigned_ssl_port)
|
||||
}),
|
||||
);
|
||||
Ok(())
|
||||
})?;
|
||||
d.as_private_mut().as_package_stores_mut().remove(&id)?;
|
||||
Ok(Some(pde))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})
|
||||
.await
|
||||
.result?
|
||||
{
|
||||
let state = pde.state_info.expect_removing()?;
|
||||
if !soft {
|
||||
for volume_id in &state.manifest.volumes {
|
||||
let path = data_dir(DATA_DIR, &state.manifest.id, volume_id);
|
||||
if tokio::fs::metadata(&path).await.is_ok() {
|
||||
tokio::fs::remove_dir_all(&path).await?;
|
||||
}
|
||||
}
|
||||
let logs_dir = Path::new(PACKAGE_DATA)
|
||||
.join("logs")
|
||||
.join(&state.manifest.id);
|
||||
if tokio::fs::metadata(&logs_dir).await.is_ok() {
|
||||
tokio::fs::remove_dir_all(&logs_dir).await?;
|
||||
}
|
||||
let archive_path = Path::new(PACKAGE_DATA)
|
||||
.join("archive")
|
||||
.join("installed")
|
||||
.join(&state.manifest.id);
|
||||
if tokio::fs::metadata(&archive_path).await.is_ok() {
|
||||
tokio::fs::remove_file(&archive_path).await?;
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -8,7 +8,7 @@ use const_format::formatcp;
|
||||
use josekit::jwk::Jwk;
|
||||
use patch_db::json_ptr::ROOT;
|
||||
use rpc_toolkit::yajrc::RpcError;
|
||||
use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler};
|
||||
use rpc_toolkit::{from_fn_async, Context, Empty, HandlerExt, ParentHandler};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::process::Command;
|
||||
@@ -32,15 +32,15 @@ use crate::disk::mount::guard::{GenericMountGuard, TmpMountGuard};
|
||||
use crate::disk::util::{pvscan, recovery_info, DiskInfo, StartOsRecoveryInfo};
|
||||
use crate::disk::REPAIR_DISK_PATH;
|
||||
use crate::init::{init, InitPhases, InitResult};
|
||||
use crate::net::net_controller::NetController;
|
||||
use crate::net::ssl::root_ca_start_time;
|
||||
use crate::prelude::*;
|
||||
use crate::progress::{FullProgress, PhaseProgressTrackerHandle};
|
||||
use crate::rpc_continuations::Guid;
|
||||
use crate::system::sync_kiosk;
|
||||
use crate::util::crypto::EncryptedWire;
|
||||
use crate::util::io::{create_file, dir_copy, dir_size, Counter};
|
||||
use crate::util::Invoke;
|
||||
use crate::{Error, ErrorKind, ResultExt, DATA_DIR, MAIN_DATA, PACKAGE_DATA};
|
||||
use crate::{Error, ErrorKind, ResultExt, DATA_DIR, MAIN_DATA, PACKAGE_DATA, PLATFORM};
|
||||
|
||||
pub fn setup<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
@@ -62,6 +62,11 @@ pub fn setup<C: Context>() -> ParentHandler<C> {
|
||||
.no_cli(),
|
||||
)
|
||||
.subcommand("exit", from_fn_async(exit).no_cli())
|
||||
.subcommand("logs", crate::system::logs::<SetupContext>())
|
||||
.subcommand(
|
||||
"logs",
|
||||
from_fn_async(crate::logs::cli_logs::<SetupContext, Empty>).no_display(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn disk<C: Context>() -> ParentHandler<C> {
|
||||
@@ -80,6 +85,7 @@ pub async fn list_disks(ctx: SetupContext) -> Result<Vec<DiskInfo>, Error> {
|
||||
async fn setup_init(
|
||||
ctx: &SetupContext,
|
||||
password: Option<String>,
|
||||
kiosk: Option<bool>,
|
||||
init_phases: InitPhases,
|
||||
) -> Result<(AccountInfo, InitResult), Error> {
|
||||
let init_result = init(&ctx.webserver, &ctx.config, init_phases).await?;
|
||||
@@ -93,15 +99,19 @@ async fn setup_init(
|
||||
account.set_password(password)?;
|
||||
}
|
||||
account.save(m)?;
|
||||
m.as_public_mut()
|
||||
.as_server_info_mut()
|
||||
.as_password_hash_mut()
|
||||
.ser(&account.password)?;
|
||||
let info = m.as_public_mut().as_server_info_mut();
|
||||
info.as_password_hash_mut().ser(&account.password)?;
|
||||
if let Some(kiosk) = kiosk {
|
||||
info.as_kiosk_mut().ser(&Some(kiosk))?;
|
||||
}
|
||||
|
||||
Ok(account)
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
|
||||
sync_kiosk(kiosk).await?;
|
||||
|
||||
if let Some(password) = &password {
|
||||
write_shadow(&password).await?;
|
||||
}
|
||||
@@ -116,6 +126,8 @@ pub struct AttachParams {
|
||||
#[serde(rename = "startOsPassword")]
|
||||
password: Option<EncryptedWire>,
|
||||
guid: Arc<String>,
|
||||
#[ts(optional)]
|
||||
kiosk: Option<bool>,
|
||||
}
|
||||
|
||||
pub async fn attach(
|
||||
@@ -123,10 +135,11 @@ pub async fn attach(
|
||||
AttachParams {
|
||||
password,
|
||||
guid: disk_guid,
|
||||
kiosk,
|
||||
}: AttachParams,
|
||||
) -> Result<SetupProgress, Error> {
|
||||
let setup_ctx = ctx.clone();
|
||||
ctx.run_setup(|| async move {
|
||||
ctx.run_setup(move || async move {
|
||||
let progress = &setup_ctx.progress;
|
||||
let mut disk_phase = progress.add_phase("Opening data drive".into(), Some(10));
|
||||
let init_phases = InitPhases::new(&progress);
|
||||
@@ -173,7 +186,7 @@ pub async fn attach(
|
||||
}
|
||||
disk_phase.complete();
|
||||
|
||||
let (account, net_ctrl) = setup_init(&setup_ctx, password, init_phases).await?;
|
||||
let (account, net_ctrl) = setup_init(&setup_ctx, password, kiosk, init_phases).await?;
|
||||
|
||||
let rpc_ctx = RpcContext::init(&setup_ctx.webserver, &setup_ctx.config, disk_guid, Some(net_ctrl), rpc_ctx_phases).await?;
|
||||
|
||||
@@ -293,6 +306,8 @@ pub struct SetupExecuteParams {
|
||||
start_os_logicalname: PathBuf,
|
||||
start_os_password: EncryptedWire,
|
||||
recovery_source: Option<RecoverySource<EncryptedWire>>,
|
||||
#[ts(optional)]
|
||||
kiosk: Option<bool>,
|
||||
}
|
||||
|
||||
// #[command(rpc_only)]
|
||||
@@ -302,6 +317,7 @@ pub async fn execute(
|
||||
start_os_logicalname,
|
||||
start_os_password,
|
||||
recovery_source,
|
||||
kiosk,
|
||||
}: SetupExecuteParams,
|
||||
) -> Result<SetupProgress, Error> {
|
||||
let start_os_password = match start_os_password.decrypt(&ctx) {
|
||||
@@ -333,7 +349,15 @@ pub async fn execute(
|
||||
};
|
||||
|
||||
let setup_ctx = ctx.clone();
|
||||
ctx.run_setup(|| execute_inner(setup_ctx, start_os_logicalname, start_os_password, recovery))?;
|
||||
ctx.run_setup(move || {
|
||||
execute_inner(
|
||||
setup_ctx,
|
||||
start_os_logicalname,
|
||||
start_os_password,
|
||||
recovery,
|
||||
kiosk,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(ctx.progress().await)
|
||||
}
|
||||
@@ -376,6 +400,7 @@ pub async fn execute_inner(
|
||||
start_os_logicalname: PathBuf,
|
||||
start_os_password: String,
|
||||
recovery_source: Option<RecoverySource<String>>,
|
||||
kiosk: Option<bool>,
|
||||
) -> Result<(SetupResult, RpcContext), Error> {
|
||||
let progress = &ctx.progress;
|
||||
let mut disk_phase = progress.add_phase("Formatting data drive".into(), Some(10));
|
||||
@@ -429,14 +454,15 @@ pub async fn execute_inner(
|
||||
target,
|
||||
server_id,
|
||||
password,
|
||||
kiosk,
|
||||
progress,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Some(RecoverySource::Migrate { guid: old_guid }) => {
|
||||
migrate(&ctx, guid, &old_guid, start_os_password, progress).await
|
||||
migrate(&ctx, guid, &old_guid, start_os_password, kiosk, progress).await
|
||||
}
|
||||
None => fresh_setup(&ctx, guid, &start_os_password, progress).await,
|
||||
None => fresh_setup(&ctx, guid, &start_os_password, kiosk, progress).await,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,6 +476,7 @@ async fn fresh_setup(
|
||||
ctx: &SetupContext,
|
||||
guid: Arc<String>,
|
||||
start_os_password: &str,
|
||||
kiosk: Option<bool>,
|
||||
SetupExecuteProgress {
|
||||
init_phases,
|
||||
rpc_ctx_phases,
|
||||
@@ -458,7 +485,9 @@ async fn fresh_setup(
|
||||
) -> Result<(SetupResult, RpcContext), Error> {
|
||||
let account = AccountInfo::new(start_os_password, root_ca_start_time().await?)?;
|
||||
let db = ctx.db().await?;
|
||||
db.put(&ROOT, &Database::init(&account)?).await?;
|
||||
let kiosk = Some(kiosk.unwrap_or(true)).filter(|_| &*PLATFORM != "raspberrypi");
|
||||
sync_kiosk(kiosk).await?;
|
||||
db.put(&ROOT, &Database::init(&account, kiosk)?).await?;
|
||||
drop(db);
|
||||
|
||||
let init_result = init(&ctx.webserver, &ctx.config, init_phases).await?;
|
||||
@@ -485,6 +514,7 @@ async fn recover(
|
||||
recovery_source: BackupTargetFS,
|
||||
server_id: String,
|
||||
recovery_password: String,
|
||||
kiosk: Option<bool>,
|
||||
progress: SetupExecuteProgress,
|
||||
) -> Result<(SetupResult, RpcContext), Error> {
|
||||
let recovery_source = TmpMountGuard::mount(&recovery_source, ReadWrite).await?;
|
||||
@@ -495,6 +525,7 @@ async fn recover(
|
||||
recovery_source,
|
||||
&server_id,
|
||||
&recovery_password,
|
||||
kiosk,
|
||||
progress,
|
||||
)
|
||||
.await
|
||||
@@ -506,6 +537,7 @@ async fn migrate(
|
||||
guid: Arc<String>,
|
||||
old_guid: &str,
|
||||
start_os_password: String,
|
||||
kiosk: Option<bool>,
|
||||
SetupExecuteProgress {
|
||||
init_phases,
|
||||
restore_phase,
|
||||
@@ -583,7 +615,7 @@ async fn migrate(
|
||||
crate::disk::main::export(&old_guid, "/media/startos/migrate").await?;
|
||||
restore_phase.complete();
|
||||
|
||||
let (account, net_ctrl) = setup_init(&ctx, Some(start_os_password), init_phases).await?;
|
||||
let (account, net_ctrl) = setup_init(&ctx, Some(start_os_password), kiosk, init_phases).await?;
|
||||
|
||||
let rpc_ctx = RpcContext::init(
|
||||
&ctx.webserver,
|
||||
|
||||
@@ -24,7 +24,6 @@ pub enum MainStatus {
|
||||
},
|
||||
Stopped,
|
||||
Restarting,
|
||||
Restoring,
|
||||
Stopping,
|
||||
Starting {
|
||||
#[ts(as = "BTreeMap<HealthCheckId, NamedHealthCheckResult>")]
|
||||
@@ -54,7 +53,6 @@ impl MainStatus {
|
||||
..
|
||||
} => true,
|
||||
MainStatus::Stopped
|
||||
| MainStatus::Restoring
|
||||
| MainStatus::Stopping { .. }
|
||||
| MainStatus::BackingUp {
|
||||
on_complete: StartStop::Stop,
|
||||
@@ -65,6 +63,13 @@ impl MainStatus {
|
||||
} => false,
|
||||
}
|
||||
}
|
||||
pub fn run_state(&self) -> StartStop {
|
||||
if self.running() {
|
||||
StartStop::Start
|
||||
} else {
|
||||
StartStop::Stop
|
||||
}
|
||||
}
|
||||
|
||||
pub fn major_changes(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
@@ -73,7 +78,6 @@ impl MainStatus {
|
||||
(MainStatus::Stopping, MainStatus::Stopping) => false,
|
||||
(MainStatus::Stopped, MainStatus::Stopped) => false,
|
||||
(MainStatus::Restarting, MainStatus::Restarting) => false,
|
||||
(MainStatus::Restoring, MainStatus::Restoring) => false,
|
||||
(MainStatus::BackingUp { .. }, MainStatus::BackingUp { .. }) => false,
|
||||
(MainStatus::Error { .. }, MainStatus::Error { .. }) => false,
|
||||
_ => true,
|
||||
@@ -95,7 +99,6 @@ impl MainStatus {
|
||||
MainStatus::Running { health, .. } | MainStatus::Starting { health } => Some(health),
|
||||
MainStatus::BackingUp { .. }
|
||||
| MainStatus::Stopped
|
||||
| MainStatus::Restoring
|
||||
| MainStatus::Stopping { .. }
|
||||
| MainStatus::Restarting
|
||||
| MainStatus::Error { .. } => None,
|
||||
|
||||
@@ -248,6 +248,75 @@ pub fn kernel_logs<C: Context + AsRef<RpcContinuations>>() -> ParentHandler<C, L
|
||||
crate::logs::logs(|_: &C, _| async { Ok(LogSource::Kernel) })
|
||||
}
|
||||
|
||||
const DISABLE_KIOSK_PATH: &str =
|
||||
"/media/startos/config/overlay/etc/systemd/system/getty@tty1.service.d/autologin.conf";
|
||||
|
||||
pub async fn sync_kiosk(kiosk: Option<bool>) -> Result<(), Error> {
|
||||
if let Some(kiosk) = kiosk {
|
||||
if kiosk {
|
||||
enable_kiosk().await?;
|
||||
} else {
|
||||
disable_kiosk().await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn enable_kiosk() -> Result<(), Error> {
|
||||
if tokio::fs::metadata(DISABLE_KIOSK_PATH).await.is_ok() {
|
||||
crate::util::io::delete_file(DISABLE_KIOSK_PATH).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn disable_kiosk() -> Result<(), Error> {
|
||||
crate::util::io::create_file(DISABLE_KIOSK_PATH)
|
||||
.await?
|
||||
.sync_all()
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn kiosk<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::<C>::new()
|
||||
.subcommand(
|
||||
"enable",
|
||||
from_fn_async(|ctx: RpcContext| async move {
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
db.as_public_mut()
|
||||
.as_server_info_mut()
|
||||
.as_kiosk_mut()
|
||||
.ser(&Some(true))
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
enable_kiosk().await
|
||||
})
|
||||
.no_display()
|
||||
.with_about("Enable kiosk mode")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand(
|
||||
"disable",
|
||||
from_fn_async(|ctx: RpcContext| async move {
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
db.as_public_mut()
|
||||
.as_server_info_mut()
|
||||
.as_kiosk_mut()
|
||||
.ser(&Some(false))
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
disable_kiosk().await
|
||||
})
|
||||
.no_display()
|
||||
.with_about("Disable kiosk mode")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct MetricLeaf<T> {
|
||||
value: T,
|
||||
|
||||
@@ -17,7 +17,7 @@ v0.4.0 is a complete rewrite of StartOS, almost nothing survived. After nearly s
|
||||
## Changelog
|
||||
|
||||
- [Improve user interface](#user-interface)
|
||||
- [Add transaltions](#translations)
|
||||
- [Add translations](#translations)
|
||||
- [Switch to lxc-based container runtime](#lxc)
|
||||
- [Update s9pk archive format](#s9pk-archive-format)
|
||||
- [Improve Actions](#actions)
|
||||
|
||||
@@ -355,6 +355,7 @@ impl VersionT for Version {
|
||||
.install(
|
||||
ctx.clone(),
|
||||
|| crate::s9pk::load(file.clone(), || Ok(key.de()?.0), None),
|
||||
None,
|
||||
None::<crate::util::Never>,
|
||||
None,
|
||||
)
|
||||
|
||||
@@ -27,7 +27,7 @@ impl VersionT for Version {
|
||||
fn compat(self) -> &'static VersionRange {
|
||||
&V0_3_0_COMPAT
|
||||
}
|
||||
fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> {
|
||||
fn up(self, _db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn down(self, _db: &mut Value) -> Result<(), Error> {
|
||||
|
||||
@@ -27,7 +27,7 @@ impl VersionT for Version {
|
||||
fn compat(self) -> &'static VersionRange {
|
||||
&V0_3_0_COMPAT
|
||||
}
|
||||
fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> {
|
||||
fn up(self, _db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn down(self, _db: &mut Value) -> Result<(), Error> {
|
||||
|
||||
@@ -2,7 +2,9 @@ use exver::{PreReleaseSegment, VersionRange};
|
||||
|
||||
use super::v0_3_5::V0_3_0_COMPAT;
|
||||
use super::{v0_4_0_alpha_3, VersionT};
|
||||
use crate::context::RpcContext;
|
||||
use crate::prelude::*;
|
||||
use crate::util::io::create_file_mod;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref V0_4_0_alpha_4: exver::Version = exver::Version::new(
|
||||
@@ -27,7 +29,76 @@ impl VersionT for Version {
|
||||
fn compat(self) -> &'static VersionRange {
|
||||
&V0_3_0_COMPAT
|
||||
}
|
||||
#[instrument]
|
||||
fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> {
|
||||
db["public"]["serverInfo"]
|
||||
.as_object_mut()
|
||||
.or_not_found("public.serverInfo")?
|
||||
.insert("kiosk".into(), Value::Bool(false));
|
||||
for (_, pde) in db["public"]["packageData"]
|
||||
.as_object_mut()
|
||||
.into_iter()
|
||||
.flat_map(|m| m.iter_mut())
|
||||
{
|
||||
let Some(pde) = pde.as_object_mut() else {
|
||||
continue;
|
||||
};
|
||||
let Some(mut tasks) = pde.remove("requestedActions").and_then(|ar| {
|
||||
if let Value::Object(ar) = ar {
|
||||
Some(ar)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
for (_, task_entry) in tasks.iter_mut() {
|
||||
let Some(task_entry) = task_entry.as_object_mut() else {
|
||||
continue;
|
||||
};
|
||||
let Some(task) = task_entry.remove("request") else {
|
||||
continue;
|
||||
};
|
||||
task_entry.insert("task".into(), task);
|
||||
}
|
||||
pde.insert("tasks".into(), Value::Object(tasks));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn post_up(self, _ctx: &RpcContext) -> Result<(), Error> {
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
if tokio::fs::metadata("/media/startos/config/overlay/etc/shadow")
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
let mut hash = None;
|
||||
let shadow_contents = tokio::fs::read_to_string("/etc/shadow").await?;
|
||||
let mut shadow_file =
|
||||
create_file_mod("/media/startos/config/overlay/etc/shadow", 0o640).await?;
|
||||
for line in shadow_contents.lines() {
|
||||
match line.split_once(":") {
|
||||
Some((user, rest)) if user == "start9" || user == "kiosk" => {
|
||||
let (h, rest) = rest.split_once(":").ok_or_else(|| {
|
||||
Error::new(eyre!("malformed /etc/shadow"), ErrorKind::ParseSysInfo)
|
||||
})?;
|
||||
if user == "start9" {
|
||||
hash = Some(h.to_owned());
|
||||
}
|
||||
let h = hash.as_deref().unwrap_or(h);
|
||||
shadow_file
|
||||
.write_all(format!("{user}:{h}:{rest}\n").as_bytes())
|
||||
.await?;
|
||||
}
|
||||
_ => {
|
||||
shadow_file.write_all(line.as_bytes()).await?;
|
||||
shadow_file.write_all(b"\n").await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
shadow_file.sync_all().await?;
|
||||
tokio::fs::copy("/media/startos/config/overlay/etc/shadow", "/etc/shadow").await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn down(self, _db: &mut Value) -> Result<(), Error> {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ExtendedVersion, VersionRange } from "./exver"
|
||||
import {
|
||||
ActionId,
|
||||
ActionInput,
|
||||
@@ -12,7 +13,7 @@ import {
|
||||
Host,
|
||||
ExportServiceInterfaceParams,
|
||||
ServiceInterface,
|
||||
RequestActionParams,
|
||||
CreateTaskParams,
|
||||
MainStatus,
|
||||
} from "./osBindings"
|
||||
import {
|
||||
@@ -50,10 +51,8 @@ export type Effects = {
|
||||
actionId: ActionId
|
||||
input?: Input
|
||||
}): Promise<ActionResult | null>
|
||||
request<Input extends Record<string, unknown>>(
|
||||
options: RequestActionParams,
|
||||
): Promise<null>
|
||||
clearRequests(
|
||||
createTask(options: CreateTaskParams): Promise<null>
|
||||
clearTasks(
|
||||
options: { only: string[] } | { except: string[] },
|
||||
): Promise<null>
|
||||
}
|
||||
@@ -168,7 +167,7 @@ export type Effects = {
|
||||
}) => Promise<string>
|
||||
|
||||
/** sets the version that this service's data has been migrated to */
|
||||
setDataVersion(options: { version: string }): Promise<null>
|
||||
setDataVersion(options: { version: string | null }): Promise<null>
|
||||
/** returns the version that this service's data has been migrated to */
|
||||
getDataVersion(): Promise<string | null>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as T from "../types"
|
||||
import * as IST from "../actions/input/inputSpecTypes"
|
||||
import { Action } from "./setupActions"
|
||||
import { Action, ActionInfo } from "./setupActions"
|
||||
import { ExtractInputSpecType } from "./input/builder/inputSpec"
|
||||
|
||||
export type RunActionInput<Input> =
|
||||
@@ -45,45 +45,41 @@ export const runAction = async <
|
||||
})
|
||||
}
|
||||
}
|
||||
type GetActionInputType<A extends Action<T.ActionId, any>> =
|
||||
type GetActionInputType<A extends ActionInfo<T.ActionId, any>> =
|
||||
A extends Action<T.ActionId, infer I> ? ExtractInputSpecType<I> : never
|
||||
|
||||
type ActionRequestBase = {
|
||||
type TaskBase = {
|
||||
reason?: string
|
||||
replayId?: string
|
||||
}
|
||||
type ActionRequestInput<T extends Action<T.ActionId, any>> = {
|
||||
type TaskInput<T extends ActionInfo<T.ActionId, any>> = {
|
||||
kind: "partial"
|
||||
value: T.DeepPartial<GetActionInputType<T>>
|
||||
}
|
||||
export type ActionRequestOptions<T extends Action<T.ActionId, any>> =
|
||||
ActionRequestBase &
|
||||
(
|
||||
| {
|
||||
when?: Exclude<
|
||||
T.ActionRequestTrigger,
|
||||
{ condition: "input-not-matches" }
|
||||
>
|
||||
input?: ActionRequestInput<T>
|
||||
}
|
||||
| {
|
||||
when: T.ActionRequestTrigger & { condition: "input-not-matches" }
|
||||
input: ActionRequestInput<T>
|
||||
}
|
||||
)
|
||||
export type TaskOptions<T extends ActionInfo<T.ActionId, any>> = TaskBase &
|
||||
(
|
||||
| {
|
||||
when?: Exclude<T.TaskTrigger, { condition: "input-not-matches" }>
|
||||
input?: TaskInput<T>
|
||||
}
|
||||
| {
|
||||
when: T.TaskTrigger & { condition: "input-not-matches" }
|
||||
input: TaskInput<T>
|
||||
}
|
||||
)
|
||||
|
||||
const _validate: T.ActionRequest = {} as ActionRequestOptions<any> & {
|
||||
const _validate: T.Task = {} as TaskOptions<any> & {
|
||||
actionId: string
|
||||
packageId: string
|
||||
severity: T.ActionSeverity
|
||||
severity: T.TaskSeverity
|
||||
}
|
||||
|
||||
export const requestAction = <T extends Action<T.ActionId, any>>(options: {
|
||||
export const createTask = <T extends ActionInfo<T.ActionId, any>>(options: {
|
||||
effects: T.Effects
|
||||
packageId: T.PackageId
|
||||
action: T
|
||||
severity: T.ActionSeverity
|
||||
options?: ActionRequestOptions<T>
|
||||
severity: T.TaskSeverity
|
||||
options?: TaskOptions<T>
|
||||
}) => {
|
||||
const request = options.options || {}
|
||||
const actionId = options.action.id
|
||||
@@ -96,5 +92,5 @@ export const requestAction = <T extends Action<T.ActionId, any>>(options: {
|
||||
replayId: request.replayId || `${options.packageId}:${actionId}`,
|
||||
}
|
||||
delete req.action
|
||||
return options.effects.action.request(req)
|
||||
return options.effects.action.createTask(req)
|
||||
}
|
||||
|
||||
@@ -919,36 +919,6 @@ export class Value<Type> {
|
||||
aVariants.validator,
|
||||
)
|
||||
}
|
||||
static filteredUnion<
|
||||
VariantValues extends {
|
||||
[K in string]: {
|
||||
name: string
|
||||
spec: InputSpec<any>
|
||||
}
|
||||
},
|
||||
>(
|
||||
getDisabledFn: LazyBuild<string[] | false | string>,
|
||||
a: {
|
||||
name: string
|
||||
description?: string | null
|
||||
warning?: string | null
|
||||
default: keyof VariantValues & string
|
||||
},
|
||||
aVariants: Variants<VariantValues>,
|
||||
) {
|
||||
return new Value<typeof aVariants.validator._TYPE>(
|
||||
async (options) => ({
|
||||
type: "union" as const,
|
||||
description: null,
|
||||
warning: null,
|
||||
...a,
|
||||
variants: await aVariants.build(options as any),
|
||||
disabled: (await getDisabledFn(options)) || false,
|
||||
immutable: false,
|
||||
}),
|
||||
aVariants.validator,
|
||||
)
|
||||
}
|
||||
static dynamicUnion<
|
||||
VariantValues extends {
|
||||
[K in string]: {
|
||||
|
||||
@@ -45,15 +45,16 @@ export const customSmtp = InputSpec.of<InputSpecOf<SmtpValue>>({
|
||||
/**
|
||||
* For service inputSpec. Gives users 3 options for SMTP: (1) disabled, (2) use system SMTP settings, (3) use custom SMTP settings
|
||||
*/
|
||||
export const smtpInputSpec = Value.filteredUnion(
|
||||
export const smtpInputSpec = Value.dynamicUnion(
|
||||
async ({ effects }) => {
|
||||
const smtp = await new GetSystemSmtp(effects).once()
|
||||
return smtp ? [] : ["system"]
|
||||
},
|
||||
{
|
||||
name: "SMTP",
|
||||
description: "Optionally provide an SMTP server for sending emails",
|
||||
default: "disabled",
|
||||
const disabled = smtp ? [] : ["system"]
|
||||
return {
|
||||
name: "SMTP",
|
||||
description: "Optionally provide an SMTP server for sending emails",
|
||||
default: "disabled",
|
||||
disabled,
|
||||
}
|
||||
},
|
||||
Variants.of({
|
||||
disabled: { name: "Disabled", spec: InputSpec.of({}) },
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from "./input/builder/inputSpec"
|
||||
import * as T from "../types"
|
||||
import { once } from "../util"
|
||||
import { InitScript } from "../inits"
|
||||
|
||||
export type Run<
|
||||
A extends Record<string, any> | InputSpec<Record<string, any>>,
|
||||
@@ -40,10 +41,20 @@ function mapMaybeFn<T, U>(
|
||||
}
|
||||
}
|
||||
|
||||
export class Action<
|
||||
export interface ActionInfo<
|
||||
Id extends T.ActionId,
|
||||
InputSpecType extends Record<string, any> | InputSpec<any>,
|
||||
> {
|
||||
readonly id: Id
|
||||
readonly _INPUT: InputSpecType
|
||||
}
|
||||
|
||||
export class Action<
|
||||
Id extends T.ActionId,
|
||||
InputSpecType extends Record<string, any> | InputSpec<any>,
|
||||
> implements ActionInfo<Id, InputSpecType>
|
||||
{
|
||||
readonly _INPUT: InputSpecType = null as any as InputSpecType
|
||||
private constructor(
|
||||
readonly id: Id,
|
||||
private readonly metadataFn: MaybeFn<T.ActionMetadata>,
|
||||
@@ -111,7 +122,8 @@ export class Action<
|
||||
|
||||
export class Actions<
|
||||
AllActions extends Record<T.ActionId, Action<T.ActionId, any>>,
|
||||
> {
|
||||
> implements InitScript
|
||||
{
|
||||
private constructor(private readonly actions: AllActions) {}
|
||||
static of(): Actions<{}> {
|
||||
return new Actions({})
|
||||
@@ -121,13 +133,26 @@ export class Actions<
|
||||
): Actions<AllActions & { [id in A["id"]]: A }> {
|
||||
return new Actions({ ...this.actions, [action.id]: action })
|
||||
}
|
||||
async update(options: { effects: T.Effects }): Promise<null> {
|
||||
async init(effects: T.Effects): Promise<void> {
|
||||
for (let action of Object.values(this.actions)) {
|
||||
await action.exportMetadata(options)
|
||||
const fn = async () => {
|
||||
let res: (value?: undefined) => void = () => {}
|
||||
const complete = new Promise((resolve) => {
|
||||
res = resolve
|
||||
})
|
||||
const e: T.Effects = effects.child(action.id)
|
||||
e.constRetry = once(() =>
|
||||
complete.then(() => fn()).catch(console.error),
|
||||
)
|
||||
try {
|
||||
await action.exportMetadata({ effects: e })
|
||||
} finally {
|
||||
res()
|
||||
}
|
||||
}
|
||||
await fn()
|
||||
}
|
||||
await options.effects.action.clear({ except: Object.keys(this.actions) })
|
||||
|
||||
return null
|
||||
await effects.action.clear({ except: Object.keys(this.actions) })
|
||||
}
|
||||
get<Id extends T.ActionId>(actionId: Id): AllActions[Id] {
|
||||
return this.actions[actionId]
|
||||
|
||||
@@ -1,208 +0,0 @@
|
||||
import * as T from "../types"
|
||||
import * as child_process from "child_process"
|
||||
import { asError } from "../util"
|
||||
|
||||
export const DEFAULT_OPTIONS: T.SyncOptions = {
|
||||
delete: true,
|
||||
exclude: [],
|
||||
}
|
||||
export type BackupSync<Volumes extends string> = {
|
||||
dataPath: `/media/startos/volumes/${Volumes}/${string}`
|
||||
backupPath: `/media/startos/backup/${string}`
|
||||
options?: Partial<T.SyncOptions>
|
||||
backupOptions?: Partial<T.SyncOptions>
|
||||
restoreOptions?: Partial<T.SyncOptions>
|
||||
}
|
||||
/**
|
||||
* This utility simplifies the volume backup process.
|
||||
* ```ts
|
||||
* export const { createBackup, restoreBackup } = Backups.volumes("main").build();
|
||||
* ```
|
||||
*
|
||||
* Changing the options of the rsync, (ie excludes) use either
|
||||
* ```ts
|
||||
* Backups.volumes("main").set_options({exclude: ['bigdata/']}).volumes('excludedVolume').build()
|
||||
* // or
|
||||
* Backups.with_options({exclude: ['bigdata/']}).volumes('excludedVolume').build()
|
||||
* ```
|
||||
*
|
||||
* Using the more fine control, using the addSets for more control
|
||||
* ```ts
|
||||
* Backups.addSets({
|
||||
* srcVolume: 'main', srcPath:'smallData/', dstPath: 'main/smallData/', dstVolume: : Backups.BACKUP
|
||||
* }, {
|
||||
* srcVolume: 'main', srcPath:'bigData/', dstPath: 'main/bigData/', dstVolume: : Backups.BACKUP, options: {exclude:['bigData/excludeThis']}}
|
||||
* ).build()q
|
||||
* ```
|
||||
*/
|
||||
export class Backups<M extends T.Manifest> {
|
||||
private constructor(
|
||||
private options = DEFAULT_OPTIONS,
|
||||
private restoreOptions: Partial<T.SyncOptions> = {},
|
||||
private backupOptions: Partial<T.SyncOptions> = {},
|
||||
private backupSet = [] as BackupSync<M["volumes"][number]>[],
|
||||
) {}
|
||||
|
||||
static withVolumes<M extends T.Manifest = never>(
|
||||
...volumeNames: Array<M["volumes"][number]>
|
||||
): Backups<M> {
|
||||
return Backups.withSyncs(
|
||||
...volumeNames.map((srcVolume) => ({
|
||||
dataPath: `/media/startos/volumes/${srcVolume}/` as const,
|
||||
backupPath: `/media/startos/backup/${srcVolume}/` as const,
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
static withSyncs<M extends T.Manifest = never>(
|
||||
...syncs: BackupSync<M["volumes"][number]>[]
|
||||
) {
|
||||
return syncs.reduce((acc, x) => acc.addSync(x), new Backups<M>())
|
||||
}
|
||||
|
||||
static withOptions<M extends T.Manifest = never>(
|
||||
options?: Partial<T.SyncOptions>,
|
||||
) {
|
||||
return new Backups<M>({ ...DEFAULT_OPTIONS, ...options })
|
||||
}
|
||||
|
||||
setOptions(options?: Partial<T.SyncOptions>) {
|
||||
this.options = {
|
||||
...this.options,
|
||||
...options,
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
setBackupOptions(options?: Partial<T.SyncOptions>) {
|
||||
this.backupOptions = {
|
||||
...this.backupOptions,
|
||||
...options,
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
setRestoreOptions(options?: Partial<T.SyncOptions>) {
|
||||
this.restoreOptions = {
|
||||
...this.restoreOptions,
|
||||
...options,
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
mountVolume(
|
||||
volume: M["volumes"][number],
|
||||
options?: Partial<{
|
||||
options: T.SyncOptions
|
||||
backupOptions: T.SyncOptions
|
||||
restoreOptions: T.SyncOptions
|
||||
}>,
|
||||
) {
|
||||
return this.addSync({
|
||||
dataPath: `/media/startos/volumes/${volume}/` as const,
|
||||
backupPath: `/media/startos/backup/${volume}/` as const,
|
||||
...options,
|
||||
})
|
||||
}
|
||||
addSync(sync: BackupSync<M["volumes"][0]>) {
|
||||
this.backupSet.push({
|
||||
...sync,
|
||||
options: { ...this.options, ...sync.options },
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
async createBackup() {
|
||||
for (const item of this.backupSet) {
|
||||
const rsyncResults = await runRsync({
|
||||
srcPath: item.dataPath,
|
||||
dstPath: item.backupPath,
|
||||
options: {
|
||||
...this.options,
|
||||
...this.backupOptions,
|
||||
...item.options,
|
||||
...item.backupOptions,
|
||||
},
|
||||
})
|
||||
await rsyncResults.wait()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
async restoreBackup() {
|
||||
for (const item of this.backupSet) {
|
||||
const rsyncResults = await runRsync({
|
||||
srcPath: item.backupPath,
|
||||
dstPath: item.dataPath,
|
||||
options: {
|
||||
...this.options,
|
||||
...this.backupOptions,
|
||||
...item.options,
|
||||
...item.backupOptions,
|
||||
},
|
||||
})
|
||||
await rsyncResults.wait()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
async function runRsync(rsyncOptions: {
|
||||
srcPath: string
|
||||
dstPath: string
|
||||
options: T.SyncOptions
|
||||
}): Promise<{
|
||||
id: () => Promise<string>
|
||||
wait: () => Promise<null>
|
||||
progress: () => Promise<number>
|
||||
}> {
|
||||
const { srcPath, dstPath, options } = rsyncOptions
|
||||
|
||||
const command = "rsync"
|
||||
const args: string[] = []
|
||||
if (options.delete) {
|
||||
args.push("--delete")
|
||||
}
|
||||
for (const exclude of options.exclude) {
|
||||
args.push(`--exclude=${exclude}`)
|
||||
}
|
||||
args.push("-actAXH")
|
||||
args.push("--info=progress2")
|
||||
args.push("--no-inc-recursive")
|
||||
args.push(srcPath)
|
||||
args.push(dstPath)
|
||||
const spawned = child_process.spawn(command, args, { detached: true })
|
||||
let percentage = 0.0
|
||||
spawned.stdout.on("data", (data: unknown) => {
|
||||
const lines = String(data).replace("\r", "\n").split("\n")
|
||||
for (const line of lines) {
|
||||
const parsed = /$([0-9.]+)%/.exec(line)?.[1]
|
||||
if (!parsed) continue
|
||||
percentage = Number.parseFloat(parsed)
|
||||
}
|
||||
})
|
||||
|
||||
spawned.stderr.on("data", (data: unknown) => {
|
||||
console.error(`Backups.runAsync`, asError(data))
|
||||
})
|
||||
|
||||
const id = async () => {
|
||||
const pid = spawned.pid
|
||||
if (pid === undefined) {
|
||||
throw new Error("rsync process has no pid")
|
||||
}
|
||||
return String(pid)
|
||||
}
|
||||
const waitPromise = new Promise<null>((resolve, reject) => {
|
||||
spawned.on("exit", (code: any) => {
|
||||
if (code === 0) {
|
||||
resolve(null)
|
||||
} else {
|
||||
reject(new Error(`rsync exited with code ${code}`))
|
||||
}
|
||||
})
|
||||
})
|
||||
const wait = () => waitPromise
|
||||
const progress = () => Promise.resolve(percentage)
|
||||
return { id, wait, progress }
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Backups } from "./Backups"
|
||||
import * as T from "../types"
|
||||
import { _ } from "../util"
|
||||
|
||||
export type SetupBackupsParams<M extends T.Manifest> =
|
||||
| M["volumes"][number][]
|
||||
| ((_: { effects: T.Effects }) => Promise<Backups<M>>)
|
||||
|
||||
type SetupBackupsRes = {
|
||||
createBackup: T.ExpectedExports.createBackup
|
||||
restoreBackup: T.ExpectedExports.restoreBackup
|
||||
}
|
||||
|
||||
export function setupBackups<M extends T.Manifest>(
|
||||
options: SetupBackupsParams<M>,
|
||||
) {
|
||||
let backupsFactory: (_: { effects: T.Effects }) => Promise<Backups<M>>
|
||||
if (options instanceof Function) {
|
||||
backupsFactory = options
|
||||
} else {
|
||||
backupsFactory = async () => Backups.withVolumes(...options)
|
||||
}
|
||||
const answer: {
|
||||
createBackup: T.ExpectedExports.createBackup
|
||||
restoreBackup: T.ExpectedExports.restoreBackup
|
||||
} = {
|
||||
get createBackup() {
|
||||
return (async (options) => {
|
||||
return (await backupsFactory(options)).createBackup()
|
||||
}) as T.ExpectedExports.createBackup
|
||||
},
|
||||
get restoreBackup() {
|
||||
return (async (options) => {
|
||||
return (await backupsFactory(options)).restoreBackup()
|
||||
}) as T.ExpectedExports.restoreBackup
|
||||
},
|
||||
}
|
||||
return answer
|
||||
}
|
||||
@@ -6,7 +6,7 @@ export type CheckDependencies<DependencyId extends PackageId = PackageId> = {
|
||||
installedSatisfied: (packageId: DependencyId) => boolean
|
||||
installedVersionSatisfied: (packageId: DependencyId) => boolean
|
||||
runningSatisfied: (packageId: DependencyId) => boolean
|
||||
actionsSatisfied: (packageId: DependencyId) => boolean
|
||||
tasksSatisfied: (packageId: DependencyId) => boolean
|
||||
healthCheckSatisfied: (
|
||||
packageId: DependencyId,
|
||||
healthCheckId: HealthCheckId,
|
||||
@@ -16,7 +16,7 @@ export type CheckDependencies<DependencyId extends PackageId = PackageId> = {
|
||||
throwIfInstalledNotSatisfied: (packageId: DependencyId) => null
|
||||
throwIfInstalledVersionNotSatisfied: (packageId: DependencyId) => null
|
||||
throwIfRunningNotSatisfied: (packageId: DependencyId) => null
|
||||
throwIfActionsNotSatisfied: (packageId: DependencyId) => null
|
||||
throwIfTasksNotSatisfied: (packageId: DependencyId) => null
|
||||
throwIfHealthNotSatisfied: (
|
||||
packageId: DependencyId,
|
||||
healthCheckId?: HealthCheckId,
|
||||
@@ -65,9 +65,9 @@ export async function checkDependencies<
|
||||
const dep = find(packageId)
|
||||
return dep.requirement.kind !== "running" || dep.result.isRunning
|
||||
}
|
||||
const actionsSatisfied = (packageId: DependencyId) =>
|
||||
Object.entries(find(packageId).result.requestedActions).filter(
|
||||
([_, req]) => req.active && req.request.severity === "critical",
|
||||
const tasksSatisfied = (packageId: DependencyId) =>
|
||||
Object.entries(find(packageId).result.tasks).filter(
|
||||
([_, t]) => t.active && t.task.severity === "critical",
|
||||
).length === 0
|
||||
const healthCheckSatisfied = (
|
||||
packageId: DependencyId,
|
||||
@@ -90,7 +90,7 @@ export async function checkDependencies<
|
||||
installedSatisfied(packageId) &&
|
||||
installedVersionSatisfied(packageId) &&
|
||||
runningSatisfied(packageId) &&
|
||||
actionsSatisfied(packageId) &&
|
||||
tasksSatisfied(packageId) &&
|
||||
healthCheckSatisfied(packageId)
|
||||
const satisfied = (packageId?: DependencyId) =>
|
||||
packageId
|
||||
@@ -129,10 +129,10 @@ export async function checkDependencies<
|
||||
}
|
||||
return null
|
||||
}
|
||||
const throwIfActionsNotSatisfied = (packageId: DependencyId) => {
|
||||
const throwIfTasksNotSatisfied = (packageId: DependencyId) => {
|
||||
const dep = find(packageId)
|
||||
const reqs = Object.entries(dep.result.requestedActions)
|
||||
.filter(([_, req]) => req.active && req.request.severity === "critical")
|
||||
const reqs = Object.entries(dep.result.tasks)
|
||||
.filter(([_, t]) => t.active && t.task.severity === "critical")
|
||||
.map(([id, _]) => id)
|
||||
if (reqs.length) {
|
||||
throw new Error(
|
||||
@@ -172,7 +172,7 @@ export async function checkDependencies<
|
||||
throwIfInstalledNotSatisfied(packageId)
|
||||
throwIfInstalledVersionNotSatisfied(packageId)
|
||||
throwIfRunningNotSatisfied(packageId)
|
||||
throwIfActionsNotSatisfied(packageId)
|
||||
throwIfTasksNotSatisfied(packageId)
|
||||
throwIfHealthNotSatisfied(packageId)
|
||||
return null
|
||||
}
|
||||
@@ -199,13 +199,13 @@ export async function checkDependencies<
|
||||
installedSatisfied,
|
||||
installedVersionSatisfied,
|
||||
runningSatisfied,
|
||||
actionsSatisfied,
|
||||
tasksSatisfied,
|
||||
healthCheckSatisfied,
|
||||
satisfied,
|
||||
throwIfInstalledNotSatisfied,
|
||||
throwIfInstalledVersionNotSatisfied,
|
||||
throwIfRunningNotSatisfied,
|
||||
throwIfActionsNotSatisfied,
|
||||
throwIfTasksNotSatisfied,
|
||||
throwIfHealthNotSatisfied,
|
||||
throwIfNotSatisfied,
|
||||
}
|
||||
|
||||
@@ -37,16 +37,10 @@ export function setupDependencies<Manifest extends T.SDKManifest>(
|
||||
fn: (options: {
|
||||
effects: T.Effects
|
||||
}) => Promise<CurrentDependenciesResult<Manifest>>,
|
||||
): (options: { effects: T.Effects }) => Promise<null> {
|
||||
const cell = { updater: async (_: { effects: T.Effects }) => null }
|
||||
cell.updater = async (options: { effects: T.Effects }) => {
|
||||
const childEffects = options.effects.child("setupDependencies")
|
||||
childEffects.constRetry = once(() => {
|
||||
cell.updater({ effects: options.effects })
|
||||
})
|
||||
|
||||
const dependencyType = await fn({ effects: childEffects })
|
||||
return await options.effects.setDependencies({
|
||||
): (effects: T.Effects) => Promise<null> {
|
||||
return async (effects: T.Effects) => {
|
||||
const dependencyType = await fn({ effects })
|
||||
return await effects.setDependencies({
|
||||
dependencies: Object.entries(dependencyType)
|
||||
.map(([k, v]) => [k, v as DependencyRequirement] as const)
|
||||
.map(
|
||||
@@ -59,5 +53,4 @@ export function setupDependencies<Manifest extends T.SDKManifest>(
|
||||
),
|
||||
})
|
||||
}
|
||||
return cell.updater
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ export * as IST from "./actions/input/inputSpecTypes"
|
||||
export * as types from "./types"
|
||||
export * as T from "./types"
|
||||
export * as yaml from "yaml"
|
||||
export * as inits from "./inits"
|
||||
export * as matches from "ts-matches"
|
||||
|
||||
export * as utils from "./util"
|
||||
|
||||
2
sdk/base/lib/inits/index.ts
Normal file
2
sdk/base/lib/inits/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./setupInit"
|
||||
export * from "./setupUninit"
|
||||
91
sdk/base/lib/inits/setupInit.ts
Normal file
91
sdk/base/lib/inits/setupInit.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { VersionRange } from "../../../base/lib/exver"
|
||||
import * as T from "../../../base/lib/types"
|
||||
import { once } from "../util"
|
||||
|
||||
export type InitKind = "install" | "update" | "restore" | null
|
||||
|
||||
export type InitFn<Kind extends InitKind = InitKind> = (
|
||||
effects: T.Effects,
|
||||
kind: Kind,
|
||||
) => Promise<void | null | undefined>
|
||||
|
||||
export interface InitScript<Kind extends InitKind = InitKind> {
|
||||
init(effects: T.Effects, kind: Kind): Promise<void>
|
||||
}
|
||||
|
||||
export type InitScriptOrFn<Kind extends InitKind = InitKind> =
|
||||
| InitScript<Kind>
|
||||
| InitFn<Kind>
|
||||
|
||||
export function setupInit(...inits: InitScriptOrFn[]): T.ExpectedExports.init {
|
||||
return async (opts) => {
|
||||
for (const idx in inits) {
|
||||
const init = inits[idx]
|
||||
const fn = async () => {
|
||||
let res: (value?: undefined) => void = () => {}
|
||||
const complete = new Promise((resolve) => {
|
||||
res = resolve
|
||||
})
|
||||
const e: T.Effects = opts.effects.child(`init_${idx}`)
|
||||
e.constRetry = once(() =>
|
||||
complete.then(() => fn()).catch(console.error),
|
||||
)
|
||||
try {
|
||||
if ("init" in init) await init.init(e, opts.kind)
|
||||
else await init(e, opts.kind)
|
||||
} finally {
|
||||
res()
|
||||
}
|
||||
}
|
||||
await fn()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setupOnInit(onInit: InitScriptOrFn): InitScript {
|
||||
return "init" in onInit
|
||||
? onInit
|
||||
: {
|
||||
init: async (effects, kind) => {
|
||||
await onInit(effects, kind)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function setupOnInstall(
|
||||
onInstall: InitScriptOrFn<"install">,
|
||||
): InitScript {
|
||||
return {
|
||||
init: async (effects, kind) => {
|
||||
if (kind === "install") {
|
||||
if ("init" in onInstall) await onInstall.init(effects, kind)
|
||||
else await onInstall(effects, kind)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function setupOnUpdate(onUpdate: InitScriptOrFn<"update">): InitScript {
|
||||
return {
|
||||
init: async (effects, kind) => {
|
||||
if (kind === "update") {
|
||||
if ("init" in onUpdate) await onUpdate.init(effects, kind)
|
||||
else await onUpdate(effects, kind)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function setupOnInstallOrUpdate(
|
||||
onInstallOrUpdate: InitScriptOrFn<"install" | "update">,
|
||||
): InitScript {
|
||||
return {
|
||||
init: async (effects, kind) => {
|
||||
if (kind === "install" || kind === "update") {
|
||||
if ("init" in onInstallOrUpdate)
|
||||
await onInstallOrUpdate.init(effects, kind)
|
||||
else await onInstallOrUpdate(effects, kind)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
25
sdk/base/lib/inits/setupUninit.ts
Normal file
25
sdk/base/lib/inits/setupUninit.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { ExtendedVersion, VersionRange } from "../../../base/lib/exver"
|
||||
import * as T from "../../../base/lib/types"
|
||||
|
||||
export type UninitFn = (
|
||||
effects: T.Effects,
|
||||
target: VersionRange | ExtendedVersion | null,
|
||||
) => Promise<void | null | undefined>
|
||||
|
||||
export interface UninitScript {
|
||||
uninit(
|
||||
effects: T.Effects,
|
||||
target: VersionRange | ExtendedVersion | null,
|
||||
): Promise<void>
|
||||
}
|
||||
|
||||
export function setupUninit(
|
||||
...uninits: (UninitScript | UninitFn)[]
|
||||
): T.ExpectedExports.uninit {
|
||||
return async (opts) => {
|
||||
for (const uninit of uninits) {
|
||||
if ("uninit" in uninit) await uninit.uninit(opts.effects, opts.target)
|
||||
else await uninit(opts.effects, opts.target)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,12 @@ export class Origin {
|
||||
readonly sslScheme: string | null,
|
||||
) {}
|
||||
|
||||
build({ username, path, search, schemeOverride }: BuildOptions): AddressInfo {
|
||||
build({
|
||||
username,
|
||||
path,
|
||||
query: search,
|
||||
schemeOverride,
|
||||
}: BuildOptions): AddressInfo {
|
||||
const qpEntries = Object.entries(search)
|
||||
.map(
|
||||
([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`,
|
||||
@@ -50,7 +55,7 @@ export class Origin {
|
||||
type,
|
||||
username,
|
||||
path,
|
||||
search,
|
||||
query: search,
|
||||
schemeOverride,
|
||||
masked,
|
||||
} = serviceInterface.options
|
||||
@@ -58,7 +63,7 @@ export class Origin {
|
||||
const addressInfo = this.build({
|
||||
username,
|
||||
path,
|
||||
search,
|
||||
query: search,
|
||||
schemeOverride,
|
||||
})
|
||||
|
||||
@@ -82,5 +87,5 @@ type BuildOptions = {
|
||||
schemeOverride: { ssl: Scheme; noSsl: Scheme } | null
|
||||
username: string | null
|
||||
path: string
|
||||
search: Record<string, string>
|
||||
query: Record<string, string>
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export class ServiceInterfaceBuilder {
|
||||
type: ServiceInterfaceType
|
||||
username: string | null
|
||||
path: string
|
||||
search: Record<string, string>
|
||||
query: Record<string, string>
|
||||
schemeOverride: { ssl: Scheme; noSsl: Scheme } | null
|
||||
masked: boolean
|
||||
},
|
||||
|
||||
@@ -10,46 +10,34 @@ export type UpdateServiceInterfacesReceipt = {
|
||||
export type ServiceInterfacesReceipt = Array<T.AddressInfo[] & AddressReceipt>
|
||||
export type SetServiceInterfaces<Output extends ServiceInterfacesReceipt> =
|
||||
(opts: { effects: T.Effects }) => Promise<Output>
|
||||
export type UpdateServiceInterfaces<Output extends ServiceInterfacesReceipt> =
|
||||
(opts: {
|
||||
effects: T.Effects
|
||||
}) => Promise<Output & UpdateServiceInterfacesReceipt>
|
||||
export type UpdateServiceInterfaces = (effects: T.Effects) => Promise<null>
|
||||
export type SetupServiceInterfaces = <Output extends ServiceInterfacesReceipt>(
|
||||
fn: SetServiceInterfaces<Output>,
|
||||
) => UpdateServiceInterfaces<Output>
|
||||
) => UpdateServiceInterfaces
|
||||
export const NO_INTERFACE_CHANGES = {} as UpdateServiceInterfacesReceipt
|
||||
export const setupServiceInterfaces: SetupServiceInterfaces = <
|
||||
Output extends ServiceInterfacesReceipt,
|
||||
>(
|
||||
fn: SetServiceInterfaces<Output>,
|
||||
) => {
|
||||
const cell = {
|
||||
updater: (async (options: { effects: T.Effects }) =>
|
||||
[] as any as Output) as UpdateServiceInterfaces<Output>,
|
||||
}
|
||||
cell.updater = (async (options: { effects: T.Effects }) => {
|
||||
const childEffects = options.effects.child("setupInterfaces")
|
||||
childEffects.constRetry = once(() => {
|
||||
cell.updater({ effects: options.effects })
|
||||
})
|
||||
return (async (effects: T.Effects) => {
|
||||
const bindings: T.BindId[] = []
|
||||
const interfaces: T.ServiceInterfaceId[] = []
|
||||
const res = await fn({
|
||||
await fn({
|
||||
effects: {
|
||||
...childEffects,
|
||||
...effects,
|
||||
bind: (params: T.BindParams) => {
|
||||
bindings.push({ id: params.id, internalPort: params.internalPort })
|
||||
return childEffects.bind(params)
|
||||
return effects.bind(params)
|
||||
},
|
||||
exportServiceInterface: (params: T.ExportServiceInterfaceParams) => {
|
||||
interfaces.push(params.id)
|
||||
return childEffects.exportServiceInterface(params)
|
||||
return effects.exportServiceInterface(params)
|
||||
},
|
||||
},
|
||||
})
|
||||
await options.effects.clearBindings({ except: bindings })
|
||||
await options.effects.clearServiceInterfaces({ except: interfaces })
|
||||
return res
|
||||
}) as UpdateServiceInterfaces<Output>
|
||||
return cell.updater
|
||||
await effects.clearBindings({ except: bindings })
|
||||
await effects.clearServiceInterfaces({ except: interfaces })
|
||||
return null
|
||||
}) as UpdateServiceInterfaces
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ActionId } from "./ActionId"
|
||||
import type { ActionRequestInput } from "./ActionRequestInput"
|
||||
import type { ActionRequestTrigger } from "./ActionRequestTrigger"
|
||||
import type { ActionSeverity } from "./ActionSeverity"
|
||||
import type { PackageId } from "./PackageId"
|
||||
|
||||
export type ActionRequest = {
|
||||
packageId: PackageId
|
||||
actionId: ActionId
|
||||
severity: ActionSeverity
|
||||
reason?: string
|
||||
when?: ActionRequestTrigger
|
||||
input?: ActionRequestInput
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ActionRequest } from "./ActionRequest"
|
||||
|
||||
export type ActionRequestEntry = { request: ActionRequest; active: boolean }
|
||||
@@ -1,7 +0,0 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ActionRequestCondition } from "./ActionRequestCondition"
|
||||
|
||||
export type ActionRequestTrigger = {
|
||||
once: boolean
|
||||
condition: ActionRequestCondition
|
||||
}
|
||||
@@ -4,4 +4,5 @@ import type { EncryptedWire } from "./EncryptedWire"
|
||||
export type AttachParams = {
|
||||
startOsPassword: EncryptedWire | null
|
||||
guid: string
|
||||
kiosk?: boolean
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ActionRequestEntry } from "./ActionRequestEntry"
|
||||
import type { HealthCheckId } from "./HealthCheckId"
|
||||
import type { NamedHealthCheckResult } from "./NamedHealthCheckResult"
|
||||
import type { PackageId } from "./PackageId"
|
||||
import type { ReplayId } from "./ReplayId"
|
||||
import type { TaskEntry } from "./TaskEntry"
|
||||
import type { Version } from "./Version"
|
||||
|
||||
export type CheckDependenciesResult = {
|
||||
@@ -12,6 +12,6 @@ export type CheckDependenciesResult = {
|
||||
installedVersion: Version | null
|
||||
satisfies: Array<Version>
|
||||
isRunning: boolean
|
||||
requestedActions: { [key: ReplayId]: ActionRequestEntry }
|
||||
tasks: { [key: ReplayId]: TaskEntry }
|
||||
healthChecks: { [key: HealthCheckId]: NamedHealthCheckResult }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type ClearActionRequestsParams =
|
||||
| { only: string[] }
|
||||
| { except: string[] }
|
||||
export type ClearTasksParams = { only: string[] } | { except: string[] }
|
||||
@@ -1,17 +1,17 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ActionId } from "./ActionId"
|
||||
import type { ActionRequestInput } from "./ActionRequestInput"
|
||||
import type { ActionRequestTrigger } from "./ActionRequestTrigger"
|
||||
import type { ActionSeverity } from "./ActionSeverity"
|
||||
import type { PackageId } from "./PackageId"
|
||||
import type { ReplayId } from "./ReplayId"
|
||||
import type { TaskInput } from "./TaskInput"
|
||||
import type { TaskSeverity } from "./TaskSeverity"
|
||||
import type { TaskTrigger } from "./TaskTrigger"
|
||||
|
||||
export type RequestActionParams = {
|
||||
export type CreateTaskParams = {
|
||||
replayId: ReplayId
|
||||
packageId: PackageId
|
||||
actionId: ActionId
|
||||
severity: ActionSeverity
|
||||
severity: TaskSeverity
|
||||
reason?: string
|
||||
when?: ActionRequestTrigger
|
||||
input?: ActionRequestInput
|
||||
when?: TaskTrigger
|
||||
input?: TaskInput
|
||||
}
|
||||
@@ -12,7 +12,6 @@ export type MainStatus =
|
||||
}
|
||||
| { main: "stopped" }
|
||||
| { main: "restarting" }
|
||||
| { main: "restoring" }
|
||||
| { main: "stopping" }
|
||||
| {
|
||||
main: "starting"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ActionId } from "./ActionId"
|
||||
import type { ActionMetadata } from "./ActionMetadata"
|
||||
import type { ActionRequestEntry } from "./ActionRequestEntry"
|
||||
import type { CurrentDependencies } from "./CurrentDependencies"
|
||||
import type { DataUrl } from "./DataUrl"
|
||||
import type { Hosts } from "./Hosts"
|
||||
@@ -9,11 +8,11 @@ import type { MainStatus } from "./MainStatus"
|
||||
import type { PackageState } from "./PackageState"
|
||||
import type { ServiceInterface } from "./ServiceInterface"
|
||||
import type { ServiceInterfaceId } from "./ServiceInterfaceId"
|
||||
import type { Version } from "./Version"
|
||||
import type { TaskEntry } from "./TaskEntry"
|
||||
|
||||
export type PackageDataEntry = {
|
||||
stateInfo: PackageState
|
||||
dataVersion: Version | null
|
||||
dataVersion: string | null
|
||||
status: MainStatus
|
||||
registry: string | null
|
||||
developerKey: string
|
||||
@@ -21,7 +20,7 @@ export type PackageDataEntry = {
|
||||
lastBackup: string | null
|
||||
currentDependencies: CurrentDependencies
|
||||
actions: { [key: ActionId]: ActionMetadata }
|
||||
requestedActions: { [key: string]: ActionRequestEntry }
|
||||
tasks: { [key: string]: TaskEntry }
|
||||
serviceInterfaces: { [key: ServiceInterfaceId]: ServiceInterface }
|
||||
hosts: Hosts
|
||||
storeExposedDependents: string[]
|
||||
|
||||
@@ -26,4 +26,5 @@ export type ServerInfo = {
|
||||
smtp: SmtpValue | null
|
||||
ram: number
|
||||
devices: Array<LshwDevice>
|
||||
kiosk: boolean | null
|
||||
}
|
||||
|
||||
@@ -6,4 +6,5 @@ export type SetupExecuteParams = {
|
||||
startOsLogicalname: string
|
||||
startOsPassword: EncryptedWire
|
||||
recoverySource: RecoverySource<EncryptedWire> | null
|
||||
kiosk?: boolean
|
||||
}
|
||||
|
||||
15
sdk/base/lib/osBindings/Task.ts
Normal file
15
sdk/base/lib/osBindings/Task.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ActionId } from "./ActionId"
|
||||
import type { PackageId } from "./PackageId"
|
||||
import type { TaskInput } from "./TaskInput"
|
||||
import type { TaskSeverity } from "./TaskSeverity"
|
||||
import type { TaskTrigger } from "./TaskTrigger"
|
||||
|
||||
export type Task = {
|
||||
packageId: PackageId
|
||||
actionId: ActionId
|
||||
severity: TaskSeverity
|
||||
reason?: string
|
||||
when?: TaskTrigger
|
||||
input?: TaskInput
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type ActionSeverity = "critical" | "important"
|
||||
export type TaskCondition = "input-not-matches"
|
||||
4
sdk/base/lib/osBindings/TaskEntry.ts
Normal file
4
sdk/base/lib/osBindings/TaskEntry.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { Task } from "./Task"
|
||||
|
||||
export type TaskEntry = { task: Task; active: boolean }
|
||||
@@ -1,6 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type ActionRequestInput = {
|
||||
kind: "partial"
|
||||
value: Record<string, unknown>
|
||||
}
|
||||
export type TaskInput = { kind: "partial"; value: Record<string, unknown> }
|
||||
@@ -1,3 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type ActionRequestCondition = "input-not-matches"
|
||||
export type TaskSeverity = "optional" | "important" | "critical"
|
||||
4
sdk/base/lib/osBindings/TaskTrigger.ts
Normal file
4
sdk/base/lib/osBindings/TaskTrigger.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { TaskCondition } from "./TaskCondition"
|
||||
|
||||
export type TaskTrigger = { once: boolean; condition: TaskCondition }
|
||||
@@ -4,17 +4,11 @@ export { AcmeSettings } from "./AcmeSettings"
|
||||
export { ActionId } from "./ActionId"
|
||||
export { ActionInput } from "./ActionInput"
|
||||
export { ActionMetadata } from "./ActionMetadata"
|
||||
export { ActionRequestCondition } from "./ActionRequestCondition"
|
||||
export { ActionRequestEntry } from "./ActionRequestEntry"
|
||||
export { ActionRequestInput } from "./ActionRequestInput"
|
||||
export { ActionRequestTrigger } from "./ActionRequestTrigger"
|
||||
export { ActionRequest } from "./ActionRequest"
|
||||
export { ActionResultMember } from "./ActionResultMember"
|
||||
export { ActionResult } from "./ActionResult"
|
||||
export { ActionResultV0 } from "./ActionResultV0"
|
||||
export { ActionResultV1 } from "./ActionResultV1"
|
||||
export { ActionResultValue } from "./ActionResultValue"
|
||||
export { ActionSeverity } from "./ActionSeverity"
|
||||
export { ActionVisibility } from "./ActionVisibility"
|
||||
export { AddAdminParams } from "./AddAdminParams"
|
||||
export { AddAssetParams } from "./AddAssetParams"
|
||||
@@ -51,14 +45,15 @@ export { Celsius } from "./Celsius"
|
||||
export { CheckDependenciesParam } from "./CheckDependenciesParam"
|
||||
export { CheckDependenciesResult } from "./CheckDependenciesResult"
|
||||
export { Cifs } from "./Cifs"
|
||||
export { ClearActionRequestsParams } from "./ClearActionRequestsParams"
|
||||
export { ClearActionsParams } from "./ClearActionsParams"
|
||||
export { ClearBindingsParams } from "./ClearBindingsParams"
|
||||
export { ClearCallbacksParams } from "./ClearCallbacksParams"
|
||||
export { ClearServiceInterfacesParams } from "./ClearServiceInterfacesParams"
|
||||
export { ClearTasksParams } from "./ClearTasksParams"
|
||||
export { CliSetIconParams } from "./CliSetIconParams"
|
||||
export { ContactInfo } from "./ContactInfo"
|
||||
export { CreateSubcontainerFsParams } from "./CreateSubcontainerFsParams"
|
||||
export { CreateTaskParams } from "./CreateTaskParams"
|
||||
export { CurrentDependencies } from "./CurrentDependencies"
|
||||
export { CurrentDependencyInfo } from "./CurrentDependencyInfo"
|
||||
export { DataUrl } from "./DataUrl"
|
||||
@@ -173,7 +168,6 @@ export { RemovePackageFromCategoryParams } from "./RemovePackageFromCategoryPara
|
||||
export { RemovePackageParams } from "./RemovePackageParams"
|
||||
export { RemoveVersionParams } from "./RemoveVersionParams"
|
||||
export { ReplayId } from "./ReplayId"
|
||||
export { RequestActionParams } from "./RequestActionParams"
|
||||
export { RequestCommitment } from "./RequestCommitment"
|
||||
export { RunActionParams } from "./RunActionParams"
|
||||
export { Security } from "./Security"
|
||||
@@ -201,6 +195,12 @@ export { SignAssetParams } from "./SignAssetParams"
|
||||
export { SignerInfo } from "./SignerInfo"
|
||||
export { SmtpValue } from "./SmtpValue"
|
||||
export { StartStop } from "./StartStop"
|
||||
export { TaskCondition } from "./TaskCondition"
|
||||
export { TaskEntry } from "./TaskEntry"
|
||||
export { TaskInput } from "./TaskInput"
|
||||
export { TaskSeverity } from "./TaskSeverity"
|
||||
export { TaskTrigger } from "./TaskTrigger"
|
||||
export { Task } from "./Task"
|
||||
export { TestSmtpParams } from "./TestSmtpParams"
|
||||
export { UnsetInboundParams } from "./UnsetInboundParams"
|
||||
export { UpdatingState } from "./UpdatingState"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Effects } from "../types"
|
||||
import {
|
||||
CheckDependenciesParam,
|
||||
ClearActionRequestsParams,
|
||||
ClearTasksParams,
|
||||
ClearActionsParams,
|
||||
ClearBindingsParams,
|
||||
ClearCallbacksParams,
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
GetActionInputParams,
|
||||
GetContainerIpParams,
|
||||
GetStatusParams,
|
||||
RequestActionParams,
|
||||
CreateTaskParams,
|
||||
RunActionParams,
|
||||
SetDataVersionParams,
|
||||
SetMainStatus,
|
||||
@@ -30,6 +30,7 @@ import { ListServiceInterfacesParams } from ".././osBindings"
|
||||
import { ExportActionParams } from ".././osBindings"
|
||||
import { MountParams } from ".././osBindings"
|
||||
import { StringObject } from "../util"
|
||||
import { ExtendedVersion, VersionRange } from "../exver"
|
||||
function typeEquality<ExpectedType>(_a: ExpectedType) {}
|
||||
|
||||
type WithCallback<T> = Omit<T, "callback"> & { callback: () => void }
|
||||
@@ -54,8 +55,8 @@ describe("startosTypeValidation ", () => {
|
||||
export: {} as ExportActionParams,
|
||||
getInput: {} as GetActionInputParams,
|
||||
run: {} as RunActionParams,
|
||||
request: {} as RequestActionParams,
|
||||
clearRequests: {} as ClearActionRequestsParams,
|
||||
createTask: {} as CreateTaskParams,
|
||||
clearTasks: {} as ClearTasksParams,
|
||||
},
|
||||
subcontainer: {
|
||||
createFs: {} as CreateSubcontainerFsParams,
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
import { Affine, StringObject, ToKebab } from "./util"
|
||||
import { Action, Actions } from "./actions/setupActions"
|
||||
import { Effects } from "./Effects"
|
||||
import { ExtendedVersion, VersionRange } from "./exver"
|
||||
export { Effects }
|
||||
export * from "./osBindings"
|
||||
export { SDKManifest } from "./types/ManifestTypes"
|
||||
@@ -38,10 +39,6 @@ export namespace ExpectedExports {
|
||||
|
||||
/** For backing up service data though the startOS UI */
|
||||
export type createBackup = (options: { effects: Effects }) => Promise<unknown>
|
||||
/** For restoring service data that was previously backed up using the startOS UI create backup flow. Backup restores are also triggered via the startOS UI, or doing a system restore flow during setup. */
|
||||
export type restoreBackup = (options: {
|
||||
effects: Effects
|
||||
}) => Promise<unknown>
|
||||
|
||||
/**
|
||||
* This is the entrypoint for the main container. Used to start up something like the service that the
|
||||
@@ -52,33 +49,20 @@ export namespace ExpectedExports {
|
||||
started(onTerm: () => PromiseLike<void>): PromiseLike<null>
|
||||
}) => Promise<DaemonBuildable>
|
||||
|
||||
/**
|
||||
* After a shutdown, if we wanted to do any operations to clean up things, like
|
||||
* set the action as unavailable or something.
|
||||
*/
|
||||
export type afterShutdown = (options: {
|
||||
effects: Effects
|
||||
}) => Promise<unknown>
|
||||
|
||||
/**
|
||||
* Every time a service launches (both on startup, and on install) this function is called before packageInit
|
||||
* Can be used to register callbacks
|
||||
*/
|
||||
export type containerInit = (options: {
|
||||
export type init = (options: {
|
||||
effects: Effects
|
||||
kind: "install" | "update" | "restore" | null
|
||||
}) => Promise<unknown>
|
||||
|
||||
/**
|
||||
* Every time a package completes an install, this function is called before the main.
|
||||
* Can be used to do migration like things.
|
||||
*/
|
||||
export type packageInit = (options: { effects: Effects }) => Promise<unknown>
|
||||
/** This will be ran during any time a package is uninstalled, for example during a update
|
||||
* this will be called.
|
||||
*/
|
||||
export type packageUninit = (options: {
|
||||
export type uninit = (options: {
|
||||
effects: Effects
|
||||
nextVersion: null | string
|
||||
target: ExtendedVersion | VersionRange | null
|
||||
}) => Promise<unknown>
|
||||
|
||||
export type manifest = Manifest
|
||||
@@ -87,12 +71,9 @@ export namespace ExpectedExports {
|
||||
}
|
||||
export type ABI = {
|
||||
createBackup: ExpectedExports.createBackup
|
||||
restoreBackup: ExpectedExports.restoreBackup
|
||||
main: ExpectedExports.main
|
||||
afterShutdown: ExpectedExports.afterShutdown
|
||||
containerInit: ExpectedExports.containerInit
|
||||
packageInit: ExpectedExports.packageInit
|
||||
packageUninit: ExpectedExports.packageUninit
|
||||
init: ExpectedExports.init
|
||||
uninit: ExpectedExports.uninit
|
||||
manifest: ExpectedExports.manifest
|
||||
actions: ExpectedExports.actions
|
||||
}
|
||||
@@ -119,8 +100,14 @@ export type SmtpValue = {
|
||||
}
|
||||
|
||||
export class UseEntrypoint {
|
||||
readonly USE_ENTRYPOINT = "USE_ENTRYPOINT"
|
||||
constructor(readonly overridCmd?: string[]) {}
|
||||
}
|
||||
export function isUseEntrypoint(
|
||||
command: CommandType,
|
||||
): command is UseEntrypoint {
|
||||
return typeof command === "object" && "ENTRYPOINT" in command
|
||||
}
|
||||
|
||||
export type CommandType = string | [string, ...string[]] | UseEntrypoint
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,362 +0,0 @@
|
||||
body,
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
font-family:
|
||||
Helvetica Neue,
|
||||
Helvetica,
|
||||
Arial;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
.small {
|
||||
font-size: 12px;
|
||||
}
|
||||
*,
|
||||
*:after,
|
||||
*:before {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 14px;
|
||||
}
|
||||
pre {
|
||||
font:
|
||||
12px/1.4 Consolas,
|
||||
"Liberation Mono",
|
||||
Menlo,
|
||||
Courier,
|
||||
monospace;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-moz-tab-size: 2;
|
||||
-o-tab-size: 2;
|
||||
tab-size: 2;
|
||||
}
|
||||
a {
|
||||
color: #0074d9;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
.space-top1 {
|
||||
padding: 10px 0 0 0;
|
||||
}
|
||||
.pad2y {
|
||||
padding: 20px 0;
|
||||
}
|
||||
.pad1y {
|
||||
padding: 10px 0;
|
||||
}
|
||||
.pad2x {
|
||||
padding: 0 20px;
|
||||
}
|
||||
.pad2 {
|
||||
padding: 20px;
|
||||
}
|
||||
.pad1 {
|
||||
padding: 10px;
|
||||
}
|
||||
.space-left2 {
|
||||
padding-left: 55px;
|
||||
}
|
||||
.space-right2 {
|
||||
padding-right: 20px;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.clearfix {
|
||||
display: block;
|
||||
}
|
||||
.clearfix:after {
|
||||
content: "";
|
||||
display: block;
|
||||
height: 0;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
}
|
||||
.fl {
|
||||
float: left;
|
||||
}
|
||||
@media only screen and (max-width: 640px) {
|
||||
.col3 {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
.hide-mobile {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.quiet {
|
||||
color: #7f7f7f;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.quiet a {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.fraction {
|
||||
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
font-size: 10px;
|
||||
color: #555;
|
||||
background: #e8e8e8;
|
||||
padding: 4px 5px;
|
||||
border-radius: 3px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div.path a:link,
|
||||
div.path a:visited {
|
||||
color: #333;
|
||||
}
|
||||
table.coverage {
|
||||
border-collapse: collapse;
|
||||
margin: 10px 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.coverage td {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
table.coverage td.line-count {
|
||||
text-align: right;
|
||||
padding: 0 5px 0 20px;
|
||||
}
|
||||
table.coverage td.line-coverage {
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
table.coverage td span.cline-any {
|
||||
display: inline-block;
|
||||
padding: 0 5px;
|
||||
width: 100%;
|
||||
}
|
||||
.missing-if-branch {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
padding: 0 4px;
|
||||
background: #333;
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.skip-if-branch {
|
||||
display: none;
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
padding: 0 4px;
|
||||
background: #ccc;
|
||||
color: white;
|
||||
}
|
||||
.missing-if-branch .typ,
|
||||
.skip-if-branch .typ {
|
||||
color: inherit !important;
|
||||
}
|
||||
.coverage-summary {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
.coverage-summary tr {
|
||||
border-bottom: 1px solid #bbb;
|
||||
}
|
||||
.keyline-all {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
.coverage-summary td,
|
||||
.coverage-summary th {
|
||||
padding: 10px;
|
||||
}
|
||||
.coverage-summary tbody {
|
||||
border: 1px solid #bbb;
|
||||
}
|
||||
.coverage-summary td {
|
||||
border-right: 1px solid #bbb;
|
||||
}
|
||||
.coverage-summary td:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
.coverage-summary th {
|
||||
text-align: left;
|
||||
font-weight: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.coverage-summary th.file {
|
||||
border-right: none !important;
|
||||
}
|
||||
.coverage-summary th.pct {
|
||||
}
|
||||
.coverage-summary th.pic,
|
||||
.coverage-summary th.abs,
|
||||
.coverage-summary td.pct,
|
||||
.coverage-summary td.abs {
|
||||
text-align: right;
|
||||
}
|
||||
.coverage-summary td.file {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.coverage-summary td.pic {
|
||||
min-width: 120px !important;
|
||||
}
|
||||
.coverage-summary tfoot td {
|
||||
}
|
||||
|
||||
.coverage-summary .sorter {
|
||||
height: 10px;
|
||||
width: 7px;
|
||||
display: inline-block;
|
||||
margin-left: 0.5em;
|
||||
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
|
||||
}
|
||||
.coverage-summary .sorted .sorter {
|
||||
background-position: 0 -20px;
|
||||
}
|
||||
.coverage-summary .sorted-desc .sorter {
|
||||
background-position: 0 -10px;
|
||||
}
|
||||
.status-line {
|
||||
height: 10px;
|
||||
}
|
||||
/* yellow */
|
||||
.cbranch-no {
|
||||
background: yellow !important;
|
||||
color: #111;
|
||||
}
|
||||
/* dark red */
|
||||
.red.solid,
|
||||
.status-line.low,
|
||||
.low .cover-fill {
|
||||
background: #c21f39;
|
||||
}
|
||||
.low .chart {
|
||||
border: 1px solid #c21f39;
|
||||
}
|
||||
.highlighted,
|
||||
.highlighted .cstat-no,
|
||||
.highlighted .fstat-no,
|
||||
.highlighted .cbranch-no {
|
||||
background: #c21f39 !important;
|
||||
}
|
||||
/* medium red */
|
||||
.cstat-no,
|
||||
.fstat-no,
|
||||
.cbranch-no,
|
||||
.cbranch-no {
|
||||
background: #f6c6ce;
|
||||
}
|
||||
/* light red */
|
||||
.low,
|
||||
.cline-no {
|
||||
background: #fce1e5;
|
||||
}
|
||||
/* light green */
|
||||
.high,
|
||||
.cline-yes {
|
||||
background: rgb(230, 245, 208);
|
||||
}
|
||||
/* medium green */
|
||||
.cstat-yes {
|
||||
background: rgb(161, 215, 106);
|
||||
}
|
||||
/* dark green */
|
||||
.status-line.high,
|
||||
.high .cover-fill {
|
||||
background: rgb(77, 146, 33);
|
||||
}
|
||||
.high .chart {
|
||||
border: 1px solid rgb(77, 146, 33);
|
||||
}
|
||||
/* dark yellow (gold) */
|
||||
.status-line.medium,
|
||||
.medium .cover-fill {
|
||||
background: #f9cd0b;
|
||||
}
|
||||
.medium .chart {
|
||||
border: 1px solid #f9cd0b;
|
||||
}
|
||||
/* light yellow */
|
||||
.medium {
|
||||
background: #fff4c2;
|
||||
}
|
||||
|
||||
.cstat-skip {
|
||||
background: #ddd;
|
||||
color: #111;
|
||||
}
|
||||
.fstat-skip {
|
||||
background: #ddd;
|
||||
color: #111 !important;
|
||||
}
|
||||
.cbranch-skip {
|
||||
background: #ddd !important;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
span.cline-neutral {
|
||||
background: #eaeaea;
|
||||
}
|
||||
|
||||
.coverage-summary td.empty {
|
||||
opacity: 0.5;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
line-height: 1;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.cover-fill,
|
||||
.cover-empty {
|
||||
display: inline-block;
|
||||
height: 12px;
|
||||
}
|
||||
.chart {
|
||||
line-height: 0;
|
||||
}
|
||||
.cover-empty {
|
||||
background: white;
|
||||
}
|
||||
.cover-full {
|
||||
border-right: none !important;
|
||||
}
|
||||
pre.prettyprint {
|
||||
border: none !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
.com {
|
||||
color: #999 !important;
|
||||
}
|
||||
.ignore-none {
|
||||
color: #999;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
min-height: 100%;
|
||||
height: auto !important;
|
||||
height: 100%;
|
||||
margin: 0 auto -48px;
|
||||
}
|
||||
.footer,
|
||||
.push {
|
||||
height: 48px;
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/* eslint-disable */
|
||||
var jumpToCode = (function init() {
|
||||
// Classes of code we would like to highlight in the file view
|
||||
var missingCoverageClasses = [".cbranch-no", ".cstat-no", ".fstat-no"];
|
||||
|
||||
// Elements to highlight in the file listing view
|
||||
var fileListingElements = ["td.pct.low"];
|
||||
|
||||
// We don't want to select elements that are direct descendants of another match
|
||||
var notSelector = ":not(" + missingCoverageClasses.join("):not(") + ") > "; // becomes `:not(a):not(b) > `
|
||||
|
||||
// Selecter that finds elements on the page to which we can jump
|
||||
var selector =
|
||||
fileListingElements.join(", ") +
|
||||
", " +
|
||||
notSelector +
|
||||
missingCoverageClasses.join(", " + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
|
||||
|
||||
// The NodeList of matching elements
|
||||
var missingCoverageElements = document.querySelectorAll(selector);
|
||||
|
||||
var currentIndex;
|
||||
|
||||
function toggleClass(index) {
|
||||
missingCoverageElements.item(currentIndex).classList.remove("highlighted");
|
||||
missingCoverageElements.item(index).classList.add("highlighted");
|
||||
}
|
||||
|
||||
function makeCurrent(index) {
|
||||
toggleClass(index);
|
||||
currentIndex = index;
|
||||
missingCoverageElements.item(index).scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "center",
|
||||
inline: "center",
|
||||
});
|
||||
}
|
||||
|
||||
function goToPrevious() {
|
||||
var nextIndex = 0;
|
||||
if (typeof currentIndex !== "number" || currentIndex === 0) {
|
||||
nextIndex = missingCoverageElements.length - 1;
|
||||
} else if (missingCoverageElements.length > 1) {
|
||||
nextIndex = currentIndex - 1;
|
||||
}
|
||||
|
||||
makeCurrent(nextIndex);
|
||||
}
|
||||
|
||||
function goToNext() {
|
||||
var nextIndex = 0;
|
||||
|
||||
if (
|
||||
typeof currentIndex === "number" &&
|
||||
currentIndex < missingCoverageElements.length - 1
|
||||
) {
|
||||
nextIndex = currentIndex + 1;
|
||||
}
|
||||
|
||||
makeCurrent(nextIndex);
|
||||
}
|
||||
|
||||
return function jump(event) {
|
||||
if (
|
||||
document.getElementById("fileSearch") === document.activeElement &&
|
||||
document.activeElement != null
|
||||
) {
|
||||
// if we're currently focused on the search input, we don't want to navigate
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.which) {
|
||||
case 78: // n
|
||||
case 74: // j
|
||||
goToNext();
|
||||
break;
|
||||
case 66: // b
|
||||
case 75: // k
|
||||
case 80: // p
|
||||
goToPrevious();
|
||||
break;
|
||||
}
|
||||
};
|
||||
})();
|
||||
window.addEventListener("keydown", jumpToCode);
|
||||
@@ -1,498 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Code coverage report for config/builder/config.ts</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="../../prettify.css" />
|
||||
<link rel="stylesheet" href="../../base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(../../sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1>
|
||||
<a href="../../index.html">All files</a> /
|
||||
<a href="index.html">config/builder</a> config.ts
|
||||
</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">78.57% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">11/14</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">0/0</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">75% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">3/4</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">78.57% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">11/14</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line medium"></div>
|
||||
<pre><table class="coverage">
|
||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
||||
<a name='L2'></a><a href='#L2'>2</a>
|
||||
<a name='L3'></a><a href='#L3'>3</a>
|
||||
<a name='L4'></a><a href='#L4'>4</a>
|
||||
<a name='L5'></a><a href='#L5'>5</a>
|
||||
<a name='L6'></a><a href='#L6'>6</a>
|
||||
<a name='L7'></a><a href='#L7'>7</a>
|
||||
<a name='L8'></a><a href='#L8'>8</a>
|
||||
<a name='L9'></a><a href='#L9'>9</a>
|
||||
<a name='L10'></a><a href='#L10'>10</a>
|
||||
<a name='L11'></a><a href='#L11'>11</a>
|
||||
<a name='L12'></a><a href='#L12'>12</a>
|
||||
<a name='L13'></a><a href='#L13'>13</a>
|
||||
<a name='L14'></a><a href='#L14'>14</a>
|
||||
<a name='L15'></a><a href='#L15'>15</a>
|
||||
<a name='L16'></a><a href='#L16'>16</a>
|
||||
<a name='L17'></a><a href='#L17'>17</a>
|
||||
<a name='L18'></a><a href='#L18'>18</a>
|
||||
<a name='L19'></a><a href='#L19'>19</a>
|
||||
<a name='L20'></a><a href='#L20'>20</a>
|
||||
<a name='L21'></a><a href='#L21'>21</a>
|
||||
<a name='L22'></a><a href='#L22'>22</a>
|
||||
<a name='L23'></a><a href='#L23'>23</a>
|
||||
<a name='L24'></a><a href='#L24'>24</a>
|
||||
<a name='L25'></a><a href='#L25'>25</a>
|
||||
<a name='L26'></a><a href='#L26'>26</a>
|
||||
<a name='L27'></a><a href='#L27'>27</a>
|
||||
<a name='L28'></a><a href='#L28'>28</a>
|
||||
<a name='L29'></a><a href='#L29'>29</a>
|
||||
<a name='L30'></a><a href='#L30'>30</a>
|
||||
<a name='L31'></a><a href='#L31'>31</a>
|
||||
<a name='L32'></a><a href='#L32'>32</a>
|
||||
<a name='L33'></a><a href='#L33'>33</a>
|
||||
<a name='L34'></a><a href='#L34'>34</a>
|
||||
<a name='L35'></a><a href='#L35'>35</a>
|
||||
<a name='L36'></a><a href='#L36'>36</a>
|
||||
<a name='L37'></a><a href='#L37'>37</a>
|
||||
<a name='L38'></a><a href='#L38'>38</a>
|
||||
<a name='L39'></a><a href='#L39'>39</a>
|
||||
<a name='L40'></a><a href='#L40'>40</a>
|
||||
<a name='L41'></a><a href='#L41'>41</a>
|
||||
<a name='L42'></a><a href='#L42'>42</a>
|
||||
<a name='L43'></a><a href='#L43'>43</a>
|
||||
<a name='L44'></a><a href='#L44'>44</a>
|
||||
<a name='L45'></a><a href='#L45'>45</a>
|
||||
<a name='L46'></a><a href='#L46'>46</a>
|
||||
<a name='L47'></a><a href='#L47'>47</a>
|
||||
<a name='L48'></a><a href='#L48'>48</a>
|
||||
<a name='L49'></a><a href='#L49'>49</a>
|
||||
<a name='L50'></a><a href='#L50'>50</a>
|
||||
<a name='L51'></a><a href='#L51'>51</a>
|
||||
<a name='L52'></a><a href='#L52'>52</a>
|
||||
<a name='L53'></a><a href='#L53'>53</a>
|
||||
<a name='L54'></a><a href='#L54'>54</a>
|
||||
<a name='L55'></a><a href='#L55'>55</a>
|
||||
<a name='L56'></a><a href='#L56'>56</a>
|
||||
<a name='L57'></a><a href='#L57'>57</a>
|
||||
<a name='L58'></a><a href='#L58'>58</a>
|
||||
<a name='L59'></a><a href='#L59'>59</a>
|
||||
<a name='L60'></a><a href='#L60'>60</a>
|
||||
<a name='L61'></a><a href='#L61'>61</a>
|
||||
<a name='L62'></a><a href='#L62'>62</a>
|
||||
<a name='L63'></a><a href='#L63'>63</a>
|
||||
<a name='L64'></a><a href='#L64'>64</a>
|
||||
<a name='L65'></a><a href='#L65'>65</a>
|
||||
<a name='L66'></a><a href='#L66'>66</a>
|
||||
<a name='L67'></a><a href='#L67'>67</a>
|
||||
<a name='L68'></a><a href='#L68'>68</a>
|
||||
<a name='L69'></a><a href='#L69'>69</a>
|
||||
<a name='L70'></a><a href='#L70'>70</a>
|
||||
<a name='L71'></a><a href='#L71'>71</a>
|
||||
<a name='L72'></a><a href='#L72'>72</a>
|
||||
<a name='L73'></a><a href='#L73'>73</a>
|
||||
<a name='L74'></a><a href='#L74'>74</a>
|
||||
<a name='L75'></a><a href='#L75'>75</a>
|
||||
<a name='L76'></a><a href='#L76'>76</a>
|
||||
<a name='L77'></a><a href='#L77'>77</a>
|
||||
<a name='L78'></a><a href='#L78'>78</a>
|
||||
<a name='L79'></a><a href='#L79'>79</a>
|
||||
<a name='L80'></a><a href='#L80'>80</a>
|
||||
<a name='L81'></a><a href='#L81'>81</a>
|
||||
<a name='L82'></a><a href='#L82'>82</a>
|
||||
<a name='L83'></a><a href='#L83'>83</a>
|
||||
<a name='L84'></a><a href='#L84'>84</a>
|
||||
<a name='L85'></a><a href='#L85'>85</a>
|
||||
<a name='L86'></a><a href='#L86'>86</a>
|
||||
<a name='L87'></a><a href='#L87'>87</a>
|
||||
<a name='L88'></a><a href='#L88'>88</a>
|
||||
<a name='L89'></a><a href='#L89'>89</a>
|
||||
<a name='L90'></a><a href='#L90'>90</a>
|
||||
<a name='L91'></a><a href='#L91'>91</a>
|
||||
<a name='L92'></a><a href='#L92'>92</a>
|
||||
<a name='L93'></a><a href='#L93'>93</a>
|
||||
<a name='L94'></a><a href='#L94'>94</a>
|
||||
<a name='L95'></a><a href='#L95'>95</a>
|
||||
<a name='L96'></a><a href='#L96'>96</a>
|
||||
<a name='L97'></a><a href='#L97'>97</a>
|
||||
<a name='L98'></a><a href='#L98'>98</a>
|
||||
<a name='L99'></a><a href='#L99'>99</a>
|
||||
<a name='L100'></a><a href='#L100'>100</a>
|
||||
<a name='L101'></a><a href='#L101'>101</a>
|
||||
<a name='L102'></a><a href='#L102'>102</a>
|
||||
<a name='L103'></a><a href='#L103'>103</a>
|
||||
<a name='L104'></a><a href='#L104'>104</a>
|
||||
<a name='L105'></a><a href='#L105'>105</a>
|
||||
<a name='L106'></a><a href='#L106'>106</a>
|
||||
<a name='L107'></a><a href='#L107'>107</a>
|
||||
<a name='L108'></a><a href='#L108'>108</a>
|
||||
<a name='L109'></a><a href='#L109'>109</a>
|
||||
<a name='L110'></a><a href='#L110'>110</a>
|
||||
<a name='L111'></a><a href='#L111'>111</a>
|
||||
<a name='L112'></a><a href='#L112'>112</a>
|
||||
<a name='L113'></a><a href='#L113'>113</a>
|
||||
<a name='L114'></a><a href='#L114'>114</a>
|
||||
<a name='L115'></a><a href='#L115'>115</a>
|
||||
<a name='L116'></a><a href='#L116'>116</a>
|
||||
<a name='L117'></a><a href='#L117'>117</a>
|
||||
<a name='L118'></a><a href='#L118'>118</a>
|
||||
<a name='L119'></a><a href='#L119'>119</a>
|
||||
<a name='L120'></a><a href='#L120'>120</a>
|
||||
<a name='L121'></a><a href='#L121'>121</a>
|
||||
<a name='L122'></a><a href='#L122'>122</a>
|
||||
<a name='L123'></a><a href='#L123'>123</a>
|
||||
<a name='L124'></a><a href='#L124'>124</a>
|
||||
<a name='L125'></a><a href='#L125'>125</a>
|
||||
<a name='L126'></a><a href='#L126'>126</a>
|
||||
<a name='L127'></a><a href='#L127'>127</a>
|
||||
<a name='L128'></a><a href='#L128'>128</a>
|
||||
<a name='L129'></a><a href='#L129'>129</a>
|
||||
<a name='L130'></a><a href='#L130'>130</a>
|
||||
<a name='L131'></a><a href='#L131'>131</a>
|
||||
<a name='L132'></a><a href='#L132'>132</a>
|
||||
<a name='L133'></a><a href='#L133'>133</a>
|
||||
<a name='L134'></a><a href='#L134'>134</a>
|
||||
<a name='L135'></a><a href='#L135'>135</a>
|
||||
<a name='L136'></a><a href='#L136'>136</a>
|
||||
<a name='L137'></a><a href='#L137'>137</a>
|
||||
<a name='L138'></a><a href='#L138'>138</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import { ValueSpec } from "../configTypes"
|
||||
import { Value } from "./value"
|
||||
import { _ } from "../../util"
|
||||
import { Effects } from "../../types"
|
||||
import { Parser, object } from "ts-matches"
|
||||
|
||||
export type LazyBuildOptions<Store> = {
|
||||
effects: Effects
|
||||
}
|
||||
export type LazyBuild<Store, ExpectedOut> = (
|
||||
options: LazyBuildOptions<Store>,
|
||||
) => Promise<ExpectedOut> | ExpectedOut
|
||||
|
||||
// prettier-ignore
|
||||
export type ExtractConfigType<A extends Record<string, any> | Config<Record<string, any>, any> | Config<Record<string, any>, never>> =
|
||||
A extends Config<infer B, any> | Config<infer B, never> ? B :
|
||||
A
|
||||
|
||||
export type ConfigSpecOf<A extends Record<string, any>, Store = never> = {
|
||||
[K in keyof A]: Value<A[K], Store>
|
||||
}
|
||||
|
||||
export type MaybeLazyValues<A> = LazyBuild<any, A> | A
|
||||
/**
|
||||
* Configs are the specs that are used by the os configuration form for this service.
|
||||
* Here is an example of a simple configuration
|
||||
```ts
|
||||
const smallConfig = Config.of({
|
||||
test: Value.boolean({
|
||||
name: "Test",
|
||||
description: "This is the description for the test",
|
||||
warning: null,
|
||||
default: false,
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
The idea of a config is that now the form is going to ask for
|
||||
Test: [ ] and the value is going to be checked as a boolean.
|
||||
There are more complex values like selects, lists, and objects. See {@link Value}
|
||||
|
||||
Also, there is the ability to get a validator/parser from this config spec.
|
||||
```ts
|
||||
const matchSmallConfig = smallConfig.validator();
|
||||
type SmallConfig = typeof matchSmallConfig._TYPE;
|
||||
```
|
||||
|
||||
Here is an example of a more complex configuration which came from a configuration for a service
|
||||
that works with bitcoin, like c-lightning.
|
||||
```ts
|
||||
|
||||
export const hostname = Value.string({
|
||||
name: "Hostname",
|
||||
default: null,
|
||||
description: "Domain or IP address of bitcoin peer",
|
||||
warning: null,
|
||||
required: true,
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
pattern:
|
||||
"(^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$)|((^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$)|(^[a-z2-7]{16}\\.onion$)|(^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$))",
|
||||
patternDescription:
|
||||
"Must be either a domain name, or an IPv4 or IPv6 address. Do not include protocol scheme (eg 'http://') or port.",
|
||||
});
|
||||
export const port = Value.number({
|
||||
name: "Port",
|
||||
default: null,
|
||||
description: "Port that peer is listening on for inbound p2p connections",
|
||||
warning: null,
|
||||
required: false,
|
||||
range: "[0,65535]",
|
||||
integral: true,
|
||||
units: null,
|
||||
placeholder: null,
|
||||
});
|
||||
export const addNodesSpec = Config.of({ hostname: hostname, port: port });
|
||||
|
||||
```
|
||||
*/
|
||||
export class Config<Type extends Record<string, any>, Store = never> {
|
||||
private constructor(
|
||||
private readonly spec: {
|
||||
[K in keyof Type]: Value<Type[K], Store> | Value<Type[K], never>
|
||||
},
|
||||
public validator: Parser<unknown, Type>,
|
||||
) {}
|
||||
async build(options: LazyBuildOptions<Store>) {
|
||||
const answer = {} as {
|
||||
[K in keyof Type]: ValueSpec
|
||||
}
|
||||
for (const k in this.spec) {
|
||||
<span class="cstat-no" title="statement not covered" > answer[k] = await this.spec[k].build(options as any)</span>
|
||||
}
|
||||
return answer
|
||||
}
|
||||
|
||||
static of<
|
||||
Spec extends Record<string, Value<any, Store> | Value<any, never>>,
|
||||
Store = never,
|
||||
>(spec: Spec) {
|
||||
const validatorObj = {} as {
|
||||
[K in keyof Spec]: Parser<unknown, any>
|
||||
}
|
||||
for (const key in spec) {
|
||||
<span class="cstat-no" title="statement not covered" > validatorObj[key] = spec[key].validator</span>
|
||||
}
|
||||
const validator = object(validatorObj)
|
||||
return new Config<
|
||||
{
|
||||
[K in keyof Spec]: Spec[K] extends
|
||||
| Value<infer T, Store>
|
||||
| Value<infer T, never>
|
||||
? T
|
||||
: never
|
||||
},
|
||||
Store
|
||||
>(spec, validator as any)
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this during the times that the input needs a more specific type.
|
||||
* Used in types that the value/ variant/ list/ config is constructed somewhere else.
|
||||
```ts
|
||||
const a = Config.text({
|
||||
name: "a",
|
||||
required: false,
|
||||
})
|
||||
|
||||
return Config.of<Store>()({
|
||||
myValue: a.withStore(),
|
||||
})
|
||||
```
|
||||
*/
|
||||
<span class="fstat-no" title="function not covered" > withStore<</span>NewStore extends Store extends never ? any : Store>() {
|
||||
<span class="cstat-no" title="statement not covered" > return this as any as Config<Type, NewStore></span>
|
||||
}
|
||||
}
|
||||
</pre></td></tr></table></pre>
|
||||
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-07-04T02:41:23.401Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="../../sorter.js"></script>
|
||||
<script src="../../block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,216 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Code coverage report for config/builder</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="../../prettify.css" />
|
||||
<link rel="stylesheet" href="../../base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(../../sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1><a href="../../index.html">All files</a> config/builder</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">31.73% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">33/104</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">0/29</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">17.18% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">11/64</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">31.37% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">32/102</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line low"></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-col="file" data-fmt="html" data-html="true" class="file">
|
||||
File
|
||||
</th>
|
||||
<th
|
||||
data-col="pic"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
data-html="true"
|
||||
class="pic"
|
||||
></th>
|
||||
<th
|
||||
data-col="statements"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Statements
|
||||
</th>
|
||||
<th
|
||||
data-col="statements_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="branches"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Branches
|
||||
</th>
|
||||
<th
|
||||
data-col="branches_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="functions"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Functions
|
||||
</th>
|
||||
<th
|
||||
data-col="functions_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="lines"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Lines
|
||||
</th>
|
||||
<th
|
||||
data-col="lines_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="file medium" data-value="config.ts">
|
||||
<a href="config.ts.html">config.ts</a>
|
||||
</td>
|
||||
<td data-value="78.57" class="pic medium">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 78%"></div>
|
||||
<div class="cover-empty" style="width: 22%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="78.57" class="pct medium">78.57%</td>
|
||||
<td data-value="14" class="abs medium">11/14</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="0" class="abs high">0/0</td>
|
||||
<td data-value="75" class="pct medium">75%</td>
|
||||
<td data-value="4" class="abs medium">3/4</td>
|
||||
<td data-value="78.57" class="pct medium">78.57%</td>
|
||||
<td data-value="14" class="abs medium">11/14</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="list.ts">
|
||||
<a href="list.ts.html">list.ts</a>
|
||||
</td>
|
||||
<td data-value="70" class="pic medium">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 70%"></div>
|
||||
<div class="cover-empty" style="width: 30%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="70" class="pct medium">70%</td>
|
||||
<td data-value="20" class="abs medium">14/20</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="0" class="abs high">0/0</td>
|
||||
<td data-value="62.5" class="pct medium">62.5%</td>
|
||||
<td data-value="8" class="abs medium">5/8</td>
|
||||
<td data-value="70" class="pct medium">70%</td>
|
||||
<td data-value="20" class="abs medium">14/20</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="value.ts">
|
||||
<a href="value.ts.html">value.ts</a>
|
||||
</td>
|
||||
<td data-value="11.42" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 11%"></div>
|
||||
<div class="cover-empty" style="width: 89%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="11.42" class="pct low">11.42%</td>
|
||||
<td data-value="70" class="abs low">8/70</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="29" class="abs low">0/29</td>
|
||||
<td data-value="5.76" class="pct low">5.76%</td>
|
||||
<td data-value="52" class="abs low">3/52</td>
|
||||
<td data-value="10.29" class="pct low">10.29%</td>
|
||||
<td data-value="68" class="abs low">7/68</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-07-04T02:41:23.401Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="../../sorter.js"></script>
|
||||
<script src="../../block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,651 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Code coverage report for config/builder/list.ts</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="../../prettify.css" />
|
||||
<link rel="stylesheet" href="../../base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(../../sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1>
|
||||
<a href="../../index.html">All files</a> /
|
||||
<a href="index.html">config/builder</a> list.ts
|
||||
</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">70% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">14/20</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">0/0</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">62.5% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">5/8</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">70% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">14/20</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line medium"></div>
|
||||
<pre><table class="coverage">
|
||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
||||
<a name='L2'></a><a href='#L2'>2</a>
|
||||
<a name='L3'></a><a href='#L3'>3</a>
|
||||
<a name='L4'></a><a href='#L4'>4</a>
|
||||
<a name='L5'></a><a href='#L5'>5</a>
|
||||
<a name='L6'></a><a href='#L6'>6</a>
|
||||
<a name='L7'></a><a href='#L7'>7</a>
|
||||
<a name='L8'></a><a href='#L8'>8</a>
|
||||
<a name='L9'></a><a href='#L9'>9</a>
|
||||
<a name='L10'></a><a href='#L10'>10</a>
|
||||
<a name='L11'></a><a href='#L11'>11</a>
|
||||
<a name='L12'></a><a href='#L12'>12</a>
|
||||
<a name='L13'></a><a href='#L13'>13</a>
|
||||
<a name='L14'></a><a href='#L14'>14</a>
|
||||
<a name='L15'></a><a href='#L15'>15</a>
|
||||
<a name='L16'></a><a href='#L16'>16</a>
|
||||
<a name='L17'></a><a href='#L17'>17</a>
|
||||
<a name='L18'></a><a href='#L18'>18</a>
|
||||
<a name='L19'></a><a href='#L19'>19</a>
|
||||
<a name='L20'></a><a href='#L20'>20</a>
|
||||
<a name='L21'></a><a href='#L21'>21</a>
|
||||
<a name='L22'></a><a href='#L22'>22</a>
|
||||
<a name='L23'></a><a href='#L23'>23</a>
|
||||
<a name='L24'></a><a href='#L24'>24</a>
|
||||
<a name='L25'></a><a href='#L25'>25</a>
|
||||
<a name='L26'></a><a href='#L26'>26</a>
|
||||
<a name='L27'></a><a href='#L27'>27</a>
|
||||
<a name='L28'></a><a href='#L28'>28</a>
|
||||
<a name='L29'></a><a href='#L29'>29</a>
|
||||
<a name='L30'></a><a href='#L30'>30</a>
|
||||
<a name='L31'></a><a href='#L31'>31</a>
|
||||
<a name='L32'></a><a href='#L32'>32</a>
|
||||
<a name='L33'></a><a href='#L33'>33</a>
|
||||
<a name='L34'></a><a href='#L34'>34</a>
|
||||
<a name='L35'></a><a href='#L35'>35</a>
|
||||
<a name='L36'></a><a href='#L36'>36</a>
|
||||
<a name='L37'></a><a href='#L37'>37</a>
|
||||
<a name='L38'></a><a href='#L38'>38</a>
|
||||
<a name='L39'></a><a href='#L39'>39</a>
|
||||
<a name='L40'></a><a href='#L40'>40</a>
|
||||
<a name='L41'></a><a href='#L41'>41</a>
|
||||
<a name='L42'></a><a href='#L42'>42</a>
|
||||
<a name='L43'></a><a href='#L43'>43</a>
|
||||
<a name='L44'></a><a href='#L44'>44</a>
|
||||
<a name='L45'></a><a href='#L45'>45</a>
|
||||
<a name='L46'></a><a href='#L46'>46</a>
|
||||
<a name='L47'></a><a href='#L47'>47</a>
|
||||
<a name='L48'></a><a href='#L48'>48</a>
|
||||
<a name='L49'></a><a href='#L49'>49</a>
|
||||
<a name='L50'></a><a href='#L50'>50</a>
|
||||
<a name='L51'></a><a href='#L51'>51</a>
|
||||
<a name='L52'></a><a href='#L52'>52</a>
|
||||
<a name='L53'></a><a href='#L53'>53</a>
|
||||
<a name='L54'></a><a href='#L54'>54</a>
|
||||
<a name='L55'></a><a href='#L55'>55</a>
|
||||
<a name='L56'></a><a href='#L56'>56</a>
|
||||
<a name='L57'></a><a href='#L57'>57</a>
|
||||
<a name='L58'></a><a href='#L58'>58</a>
|
||||
<a name='L59'></a><a href='#L59'>59</a>
|
||||
<a name='L60'></a><a href='#L60'>60</a>
|
||||
<a name='L61'></a><a href='#L61'>61</a>
|
||||
<a name='L62'></a><a href='#L62'>62</a>
|
||||
<a name='L63'></a><a href='#L63'>63</a>
|
||||
<a name='L64'></a><a href='#L64'>64</a>
|
||||
<a name='L65'></a><a href='#L65'>65</a>
|
||||
<a name='L66'></a><a href='#L66'>66</a>
|
||||
<a name='L67'></a><a href='#L67'>67</a>
|
||||
<a name='L68'></a><a href='#L68'>68</a>
|
||||
<a name='L69'></a><a href='#L69'>69</a>
|
||||
<a name='L70'></a><a href='#L70'>70</a>
|
||||
<a name='L71'></a><a href='#L71'>71</a>
|
||||
<a name='L72'></a><a href='#L72'>72</a>
|
||||
<a name='L73'></a><a href='#L73'>73</a>
|
||||
<a name='L74'></a><a href='#L74'>74</a>
|
||||
<a name='L75'></a><a href='#L75'>75</a>
|
||||
<a name='L76'></a><a href='#L76'>76</a>
|
||||
<a name='L77'></a><a href='#L77'>77</a>
|
||||
<a name='L78'></a><a href='#L78'>78</a>
|
||||
<a name='L79'></a><a href='#L79'>79</a>
|
||||
<a name='L80'></a><a href='#L80'>80</a>
|
||||
<a name='L81'></a><a href='#L81'>81</a>
|
||||
<a name='L82'></a><a href='#L82'>82</a>
|
||||
<a name='L83'></a><a href='#L83'>83</a>
|
||||
<a name='L84'></a><a href='#L84'>84</a>
|
||||
<a name='L85'></a><a href='#L85'>85</a>
|
||||
<a name='L86'></a><a href='#L86'>86</a>
|
||||
<a name='L87'></a><a href='#L87'>87</a>
|
||||
<a name='L88'></a><a href='#L88'>88</a>
|
||||
<a name='L89'></a><a href='#L89'>89</a>
|
||||
<a name='L90'></a><a href='#L90'>90</a>
|
||||
<a name='L91'></a><a href='#L91'>91</a>
|
||||
<a name='L92'></a><a href='#L92'>92</a>
|
||||
<a name='L93'></a><a href='#L93'>93</a>
|
||||
<a name='L94'></a><a href='#L94'>94</a>
|
||||
<a name='L95'></a><a href='#L95'>95</a>
|
||||
<a name='L96'></a><a href='#L96'>96</a>
|
||||
<a name='L97'></a><a href='#L97'>97</a>
|
||||
<a name='L98'></a><a href='#L98'>98</a>
|
||||
<a name='L99'></a><a href='#L99'>99</a>
|
||||
<a name='L100'></a><a href='#L100'>100</a>
|
||||
<a name='L101'></a><a href='#L101'>101</a>
|
||||
<a name='L102'></a><a href='#L102'>102</a>
|
||||
<a name='L103'></a><a href='#L103'>103</a>
|
||||
<a name='L104'></a><a href='#L104'>104</a>
|
||||
<a name='L105'></a><a href='#L105'>105</a>
|
||||
<a name='L106'></a><a href='#L106'>106</a>
|
||||
<a name='L107'></a><a href='#L107'>107</a>
|
||||
<a name='L108'></a><a href='#L108'>108</a>
|
||||
<a name='L109'></a><a href='#L109'>109</a>
|
||||
<a name='L110'></a><a href='#L110'>110</a>
|
||||
<a name='L111'></a><a href='#L111'>111</a>
|
||||
<a name='L112'></a><a href='#L112'>112</a>
|
||||
<a name='L113'></a><a href='#L113'>113</a>
|
||||
<a name='L114'></a><a href='#L114'>114</a>
|
||||
<a name='L115'></a><a href='#L115'>115</a>
|
||||
<a name='L116'></a><a href='#L116'>116</a>
|
||||
<a name='L117'></a><a href='#L117'>117</a>
|
||||
<a name='L118'></a><a href='#L118'>118</a>
|
||||
<a name='L119'></a><a href='#L119'>119</a>
|
||||
<a name='L120'></a><a href='#L120'>120</a>
|
||||
<a name='L121'></a><a href='#L121'>121</a>
|
||||
<a name='L122'></a><a href='#L122'>122</a>
|
||||
<a name='L123'></a><a href='#L123'>123</a>
|
||||
<a name='L124'></a><a href='#L124'>124</a>
|
||||
<a name='L125'></a><a href='#L125'>125</a>
|
||||
<a name='L126'></a><a href='#L126'>126</a>
|
||||
<a name='L127'></a><a href='#L127'>127</a>
|
||||
<a name='L128'></a><a href='#L128'>128</a>
|
||||
<a name='L129'></a><a href='#L129'>129</a>
|
||||
<a name='L130'></a><a href='#L130'>130</a>
|
||||
<a name='L131'></a><a href='#L131'>131</a>
|
||||
<a name='L132'></a><a href='#L132'>132</a>
|
||||
<a name='L133'></a><a href='#L133'>133</a>
|
||||
<a name='L134'></a><a href='#L134'>134</a>
|
||||
<a name='L135'></a><a href='#L135'>135</a>
|
||||
<a name='L136'></a><a href='#L136'>136</a>
|
||||
<a name='L137'></a><a href='#L137'>137</a>
|
||||
<a name='L138'></a><a href='#L138'>138</a>
|
||||
<a name='L139'></a><a href='#L139'>139</a>
|
||||
<a name='L140'></a><a href='#L140'>140</a>
|
||||
<a name='L141'></a><a href='#L141'>141</a>
|
||||
<a name='L142'></a><a href='#L142'>142</a>
|
||||
<a name='L143'></a><a href='#L143'>143</a>
|
||||
<a name='L144'></a><a href='#L144'>144</a>
|
||||
<a name='L145'></a><a href='#L145'>145</a>
|
||||
<a name='L146'></a><a href='#L146'>146</a>
|
||||
<a name='L147'></a><a href='#L147'>147</a>
|
||||
<a name='L148'></a><a href='#L148'>148</a>
|
||||
<a name='L149'></a><a href='#L149'>149</a>
|
||||
<a name='L150'></a><a href='#L150'>150</a>
|
||||
<a name='L151'></a><a href='#L151'>151</a>
|
||||
<a name='L152'></a><a href='#L152'>152</a>
|
||||
<a name='L153'></a><a href='#L153'>153</a>
|
||||
<a name='L154'></a><a href='#L154'>154</a>
|
||||
<a name='L155'></a><a href='#L155'>155</a>
|
||||
<a name='L156'></a><a href='#L156'>156</a>
|
||||
<a name='L157'></a><a href='#L157'>157</a>
|
||||
<a name='L158'></a><a href='#L158'>158</a>
|
||||
<a name='L159'></a><a href='#L159'>159</a>
|
||||
<a name='L160'></a><a href='#L160'>160</a>
|
||||
<a name='L161'></a><a href='#L161'>161</a>
|
||||
<a name='L162'></a><a href='#L162'>162</a>
|
||||
<a name='L163'></a><a href='#L163'>163</a>
|
||||
<a name='L164'></a><a href='#L164'>164</a>
|
||||
<a name='L165'></a><a href='#L165'>165</a>
|
||||
<a name='L166'></a><a href='#L166'>166</a>
|
||||
<a name='L167'></a><a href='#L167'>167</a>
|
||||
<a name='L168'></a><a href='#L168'>168</a>
|
||||
<a name='L169'></a><a href='#L169'>169</a>
|
||||
<a name='L170'></a><a href='#L170'>170</a>
|
||||
<a name='L171'></a><a href='#L171'>171</a>
|
||||
<a name='L172'></a><a href='#L172'>172</a>
|
||||
<a name='L173'></a><a href='#L173'>173</a>
|
||||
<a name='L174'></a><a href='#L174'>174</a>
|
||||
<a name='L175'></a><a href='#L175'>175</a>
|
||||
<a name='L176'></a><a href='#L176'>176</a>
|
||||
<a name='L177'></a><a href='#L177'>177</a>
|
||||
<a name='L178'></a><a href='#L178'>178</a>
|
||||
<a name='L179'></a><a href='#L179'>179</a>
|
||||
<a name='L180'></a><a href='#L180'>180</a>
|
||||
<a name='L181'></a><a href='#L181'>181</a>
|
||||
<a name='L182'></a><a href='#L182'>182</a>
|
||||
<a name='L183'></a><a href='#L183'>183</a>
|
||||
<a name='L184'></a><a href='#L184'>184</a>
|
||||
<a name='L185'></a><a href='#L185'>185</a>
|
||||
<a name='L186'></a><a href='#L186'>186</a>
|
||||
<a name='L187'></a><a href='#L187'>187</a>
|
||||
<a name='L188'></a><a href='#L188'>188</a>
|
||||
<a name='L189'></a><a href='#L189'>189</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import { Config, LazyBuild } from "./config"
|
||||
import {
|
||||
ListValueSpecText,
|
||||
Pattern,
|
||||
RandomString,
|
||||
UniqueBy,
|
||||
ValueSpecList,
|
||||
ValueSpecListOf,
|
||||
} from "../configTypes"
|
||||
import { Parser, arrayOf, number, string } from "ts-matches"
|
||||
/**
|
||||
* Used as a subtype of Value.list
|
||||
```ts
|
||||
export const authorizationList = List.string({
|
||||
"name": "Authorization",
|
||||
"range": "[0,*)",
|
||||
"default": [],
|
||||
"description": "Username and hashed password for JSON-RPC connections. RPC clients connect using the usual http basic authentication.",
|
||||
"warning": null
|
||||
}, {"masked":false,"placeholder":null,"pattern":"^[a-zA-Z0-9_-]+:([0-9a-fA-F]{2})+\\$([0-9a-fA-F]{2})+$","patternDescription":"Each item must be of the form \"<USERNAME>:<SALT>$<HASH>\"."});
|
||||
export const auth = Value.list(authorizationList);
|
||||
```
|
||||
*/
|
||||
export class List<Type, Store> {
|
||||
private constructor(
|
||||
public build: LazyBuild<Store, ValueSpecList>,
|
||||
public validator: Parser<unknown, Type>,
|
||||
) {}
|
||||
static text(
|
||||
a: {
|
||||
name: string
|
||||
description?: string | null
|
||||
warning?: string | null
|
||||
/** Default = [] */
|
||||
default?: string[]
|
||||
minLength?: number | null
|
||||
maxLength?: number | null
|
||||
},
|
||||
aSpec: {
|
||||
/** Default = false */
|
||||
masked?: boolean
|
||||
placeholder?: string | null
|
||||
minLength?: number | null
|
||||
maxLength?: number | null
|
||||
patterns: Pattern[]
|
||||
/** Default = "text" */
|
||||
inputmode?: ListValueSpecText["inputmode"]
|
||||
generate?: null | RandomString
|
||||
},
|
||||
) {
|
||||
return new List<string[], never>(() => {
|
||||
const spec = {
|
||||
type: "text" as const,
|
||||
placeholder: null,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
masked: false,
|
||||
inputmode: "text" as const,
|
||||
generate: null,
|
||||
...aSpec,
|
||||
}
|
||||
const built: ValueSpecListOf<"text"> = {
|
||||
description: null,
|
||||
warning: null,
|
||||
default: [],
|
||||
type: "list" as const,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
disabled: false,
|
||||
...a,
|
||||
spec,
|
||||
}
|
||||
return built
|
||||
}, arrayOf(string))
|
||||
}
|
||||
<span class="fstat-no" title="function not covered" > static </span>dynamicText<Store = never>(
|
||||
getA: LazyBuild<
|
||||
Store,
|
||||
{
|
||||
name: string
|
||||
description?: string | null
|
||||
warning?: string | null
|
||||
/** Default = [] */
|
||||
default?: string[]
|
||||
minLength?: number | null
|
||||
maxLength?: number | null
|
||||
disabled?: false | string
|
||||
generate?: null | RandomString
|
||||
spec: {
|
||||
/** Default = false */
|
||||
masked?: boolean
|
||||
placeholder?: string | null
|
||||
minLength?: number | null
|
||||
maxLength?: number | null
|
||||
patterns: Pattern[]
|
||||
/** Default = "text" */
|
||||
inputmode?: ListValueSpecText["inputmode"]
|
||||
}
|
||||
}
|
||||
>,
|
||||
) {
|
||||
<span class="cstat-no" title="statement not covered" > return new List<string[], Store>(<span class="fstat-no" title="function not covered" >async </span>(options) => {</span>
|
||||
const { spec: aSpec, ...a } = <span class="cstat-no" title="statement not covered" >await getA(options)</span>
|
||||
const spec = <span class="cstat-no" title="statement not covered" >{</span>
|
||||
type: "text" as const,
|
||||
placeholder: null,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
masked: false,
|
||||
inputmode: "text" as const,
|
||||
generate: null,
|
||||
...aSpec,
|
||||
}
|
||||
const built: ValueSpecListOf<"text"> = <span class="cstat-no" title="statement not covered" >{</span>
|
||||
description: null,
|
||||
warning: null,
|
||||
default: [],
|
||||
type: "list" as const,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
disabled: false,
|
||||
...a,
|
||||
spec,
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > return built</span>
|
||||
}, arrayOf(string))
|
||||
}
|
||||
static obj<Type extends Record<string, any>, Store>(
|
||||
a: {
|
||||
name: string
|
||||
description?: string | null
|
||||
warning?: string | null
|
||||
/** Default [] */
|
||||
default?: []
|
||||
minLength?: number | null
|
||||
maxLength?: number | null
|
||||
},
|
||||
aSpec: {
|
||||
spec: Config<Type, Store>
|
||||
displayAs?: null | string
|
||||
uniqueBy?: null | UniqueBy
|
||||
},
|
||||
) {
|
||||
return new List<Type[], Store>(async (options) => {
|
||||
const { spec: previousSpecSpec, ...restSpec } = aSpec
|
||||
const specSpec = await previousSpecSpec.build(options)
|
||||
const spec = {
|
||||
type: "object" as const,
|
||||
displayAs: null,
|
||||
uniqueBy: null,
|
||||
...restSpec,
|
||||
spec: specSpec,
|
||||
}
|
||||
const value = {
|
||||
spec,
|
||||
default: [],
|
||||
...a,
|
||||
}
|
||||
return {
|
||||
description: null,
|
||||
warning: null,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
type: "list" as const,
|
||||
disabled: false,
|
||||
...value,
|
||||
}
|
||||
}, arrayOf(aSpec.spec.validator))
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this during the times that the input needs a more specific type.
|
||||
* Used in types that the value/ variant/ list/ config is constructed somewhere else.
|
||||
```ts
|
||||
const a = Config.text({
|
||||
name: "a",
|
||||
required: false,
|
||||
})
|
||||
|
||||
return Config.of<Store>()({
|
||||
myValue: a.withStore(),
|
||||
})
|
||||
```
|
||||
*/
|
||||
<span class="fstat-no" title="function not covered" > withStore<</span>NewStore extends Store extends never ? any : Store>() {
|
||||
<span class="cstat-no" title="statement not covered" > return this as any as List<Type, NewStore></span>
|
||||
}
|
||||
}
|
||||
</pre></td></tr></table></pre>
|
||||
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-07-04T02:41:23.401Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="../../sorter.js"></script>
|
||||
<script src="../../block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,921 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Code coverage report for config/configTypes.ts</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="../prettify.css" />
|
||||
<link rel="stylesheet" href="../base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(../sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1>
|
||||
<a href="../index.html">All files</a> /
|
||||
<a href="index.html">config</a> configTypes.ts
|
||||
</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">4/4</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">2/2</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">1/1</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">4/4</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line high"></div>
|
||||
<pre><table class="coverage">
|
||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
||||
<a name='L2'></a><a href='#L2'>2</a>
|
||||
<a name='L3'></a><a href='#L3'>3</a>
|
||||
<a name='L4'></a><a href='#L4'>4</a>
|
||||
<a name='L5'></a><a href='#L5'>5</a>
|
||||
<a name='L6'></a><a href='#L6'>6</a>
|
||||
<a name='L7'></a><a href='#L7'>7</a>
|
||||
<a name='L8'></a><a href='#L8'>8</a>
|
||||
<a name='L9'></a><a href='#L9'>9</a>
|
||||
<a name='L10'></a><a href='#L10'>10</a>
|
||||
<a name='L11'></a><a href='#L11'>11</a>
|
||||
<a name='L12'></a><a href='#L12'>12</a>
|
||||
<a name='L13'></a><a href='#L13'>13</a>
|
||||
<a name='L14'></a><a href='#L14'>14</a>
|
||||
<a name='L15'></a><a href='#L15'>15</a>
|
||||
<a name='L16'></a><a href='#L16'>16</a>
|
||||
<a name='L17'></a><a href='#L17'>17</a>
|
||||
<a name='L18'></a><a href='#L18'>18</a>
|
||||
<a name='L19'></a><a href='#L19'>19</a>
|
||||
<a name='L20'></a><a href='#L20'>20</a>
|
||||
<a name='L21'></a><a href='#L21'>21</a>
|
||||
<a name='L22'></a><a href='#L22'>22</a>
|
||||
<a name='L23'></a><a href='#L23'>23</a>
|
||||
<a name='L24'></a><a href='#L24'>24</a>
|
||||
<a name='L25'></a><a href='#L25'>25</a>
|
||||
<a name='L26'></a><a href='#L26'>26</a>
|
||||
<a name='L27'></a><a href='#L27'>27</a>
|
||||
<a name='L28'></a><a href='#L28'>28</a>
|
||||
<a name='L29'></a><a href='#L29'>29</a>
|
||||
<a name='L30'></a><a href='#L30'>30</a>
|
||||
<a name='L31'></a><a href='#L31'>31</a>
|
||||
<a name='L32'></a><a href='#L32'>32</a>
|
||||
<a name='L33'></a><a href='#L33'>33</a>
|
||||
<a name='L34'></a><a href='#L34'>34</a>
|
||||
<a name='L35'></a><a href='#L35'>35</a>
|
||||
<a name='L36'></a><a href='#L36'>36</a>
|
||||
<a name='L37'></a><a href='#L37'>37</a>
|
||||
<a name='L38'></a><a href='#L38'>38</a>
|
||||
<a name='L39'></a><a href='#L39'>39</a>
|
||||
<a name='L40'></a><a href='#L40'>40</a>
|
||||
<a name='L41'></a><a href='#L41'>41</a>
|
||||
<a name='L42'></a><a href='#L42'>42</a>
|
||||
<a name='L43'></a><a href='#L43'>43</a>
|
||||
<a name='L44'></a><a href='#L44'>44</a>
|
||||
<a name='L45'></a><a href='#L45'>45</a>
|
||||
<a name='L46'></a><a href='#L46'>46</a>
|
||||
<a name='L47'></a><a href='#L47'>47</a>
|
||||
<a name='L48'></a><a href='#L48'>48</a>
|
||||
<a name='L49'></a><a href='#L49'>49</a>
|
||||
<a name='L50'></a><a href='#L50'>50</a>
|
||||
<a name='L51'></a><a href='#L51'>51</a>
|
||||
<a name='L52'></a><a href='#L52'>52</a>
|
||||
<a name='L53'></a><a href='#L53'>53</a>
|
||||
<a name='L54'></a><a href='#L54'>54</a>
|
||||
<a name='L55'></a><a href='#L55'>55</a>
|
||||
<a name='L56'></a><a href='#L56'>56</a>
|
||||
<a name='L57'></a><a href='#L57'>57</a>
|
||||
<a name='L58'></a><a href='#L58'>58</a>
|
||||
<a name='L59'></a><a href='#L59'>59</a>
|
||||
<a name='L60'></a><a href='#L60'>60</a>
|
||||
<a name='L61'></a><a href='#L61'>61</a>
|
||||
<a name='L62'></a><a href='#L62'>62</a>
|
||||
<a name='L63'></a><a href='#L63'>63</a>
|
||||
<a name='L64'></a><a href='#L64'>64</a>
|
||||
<a name='L65'></a><a href='#L65'>65</a>
|
||||
<a name='L66'></a><a href='#L66'>66</a>
|
||||
<a name='L67'></a><a href='#L67'>67</a>
|
||||
<a name='L68'></a><a href='#L68'>68</a>
|
||||
<a name='L69'></a><a href='#L69'>69</a>
|
||||
<a name='L70'></a><a href='#L70'>70</a>
|
||||
<a name='L71'></a><a href='#L71'>71</a>
|
||||
<a name='L72'></a><a href='#L72'>72</a>
|
||||
<a name='L73'></a><a href='#L73'>73</a>
|
||||
<a name='L74'></a><a href='#L74'>74</a>
|
||||
<a name='L75'></a><a href='#L75'>75</a>
|
||||
<a name='L76'></a><a href='#L76'>76</a>
|
||||
<a name='L77'></a><a href='#L77'>77</a>
|
||||
<a name='L78'></a><a href='#L78'>78</a>
|
||||
<a name='L79'></a><a href='#L79'>79</a>
|
||||
<a name='L80'></a><a href='#L80'>80</a>
|
||||
<a name='L81'></a><a href='#L81'>81</a>
|
||||
<a name='L82'></a><a href='#L82'>82</a>
|
||||
<a name='L83'></a><a href='#L83'>83</a>
|
||||
<a name='L84'></a><a href='#L84'>84</a>
|
||||
<a name='L85'></a><a href='#L85'>85</a>
|
||||
<a name='L86'></a><a href='#L86'>86</a>
|
||||
<a name='L87'></a><a href='#L87'>87</a>
|
||||
<a name='L88'></a><a href='#L88'>88</a>
|
||||
<a name='L89'></a><a href='#L89'>89</a>
|
||||
<a name='L90'></a><a href='#L90'>90</a>
|
||||
<a name='L91'></a><a href='#L91'>91</a>
|
||||
<a name='L92'></a><a href='#L92'>92</a>
|
||||
<a name='L93'></a><a href='#L93'>93</a>
|
||||
<a name='L94'></a><a href='#L94'>94</a>
|
||||
<a name='L95'></a><a href='#L95'>95</a>
|
||||
<a name='L96'></a><a href='#L96'>96</a>
|
||||
<a name='L97'></a><a href='#L97'>97</a>
|
||||
<a name='L98'></a><a href='#L98'>98</a>
|
||||
<a name='L99'></a><a href='#L99'>99</a>
|
||||
<a name='L100'></a><a href='#L100'>100</a>
|
||||
<a name='L101'></a><a href='#L101'>101</a>
|
||||
<a name='L102'></a><a href='#L102'>102</a>
|
||||
<a name='L103'></a><a href='#L103'>103</a>
|
||||
<a name='L104'></a><a href='#L104'>104</a>
|
||||
<a name='L105'></a><a href='#L105'>105</a>
|
||||
<a name='L106'></a><a href='#L106'>106</a>
|
||||
<a name='L107'></a><a href='#L107'>107</a>
|
||||
<a name='L108'></a><a href='#L108'>108</a>
|
||||
<a name='L109'></a><a href='#L109'>109</a>
|
||||
<a name='L110'></a><a href='#L110'>110</a>
|
||||
<a name='L111'></a><a href='#L111'>111</a>
|
||||
<a name='L112'></a><a href='#L112'>112</a>
|
||||
<a name='L113'></a><a href='#L113'>113</a>
|
||||
<a name='L114'></a><a href='#L114'>114</a>
|
||||
<a name='L115'></a><a href='#L115'>115</a>
|
||||
<a name='L116'></a><a href='#L116'>116</a>
|
||||
<a name='L117'></a><a href='#L117'>117</a>
|
||||
<a name='L118'></a><a href='#L118'>118</a>
|
||||
<a name='L119'></a><a href='#L119'>119</a>
|
||||
<a name='L120'></a><a href='#L120'>120</a>
|
||||
<a name='L121'></a><a href='#L121'>121</a>
|
||||
<a name='L122'></a><a href='#L122'>122</a>
|
||||
<a name='L123'></a><a href='#L123'>123</a>
|
||||
<a name='L124'></a><a href='#L124'>124</a>
|
||||
<a name='L125'></a><a href='#L125'>125</a>
|
||||
<a name='L126'></a><a href='#L126'>126</a>
|
||||
<a name='L127'></a><a href='#L127'>127</a>
|
||||
<a name='L128'></a><a href='#L128'>128</a>
|
||||
<a name='L129'></a><a href='#L129'>129</a>
|
||||
<a name='L130'></a><a href='#L130'>130</a>
|
||||
<a name='L131'></a><a href='#L131'>131</a>
|
||||
<a name='L132'></a><a href='#L132'>132</a>
|
||||
<a name='L133'></a><a href='#L133'>133</a>
|
||||
<a name='L134'></a><a href='#L134'>134</a>
|
||||
<a name='L135'></a><a href='#L135'>135</a>
|
||||
<a name='L136'></a><a href='#L136'>136</a>
|
||||
<a name='L137'></a><a href='#L137'>137</a>
|
||||
<a name='L138'></a><a href='#L138'>138</a>
|
||||
<a name='L139'></a><a href='#L139'>139</a>
|
||||
<a name='L140'></a><a href='#L140'>140</a>
|
||||
<a name='L141'></a><a href='#L141'>141</a>
|
||||
<a name='L142'></a><a href='#L142'>142</a>
|
||||
<a name='L143'></a><a href='#L143'>143</a>
|
||||
<a name='L144'></a><a href='#L144'>144</a>
|
||||
<a name='L145'></a><a href='#L145'>145</a>
|
||||
<a name='L146'></a><a href='#L146'>146</a>
|
||||
<a name='L147'></a><a href='#L147'>147</a>
|
||||
<a name='L148'></a><a href='#L148'>148</a>
|
||||
<a name='L149'></a><a href='#L149'>149</a>
|
||||
<a name='L150'></a><a href='#L150'>150</a>
|
||||
<a name='L151'></a><a href='#L151'>151</a>
|
||||
<a name='L152'></a><a href='#L152'>152</a>
|
||||
<a name='L153'></a><a href='#L153'>153</a>
|
||||
<a name='L154'></a><a href='#L154'>154</a>
|
||||
<a name='L155'></a><a href='#L155'>155</a>
|
||||
<a name='L156'></a><a href='#L156'>156</a>
|
||||
<a name='L157'></a><a href='#L157'>157</a>
|
||||
<a name='L158'></a><a href='#L158'>158</a>
|
||||
<a name='L159'></a><a href='#L159'>159</a>
|
||||
<a name='L160'></a><a href='#L160'>160</a>
|
||||
<a name='L161'></a><a href='#L161'>161</a>
|
||||
<a name='L162'></a><a href='#L162'>162</a>
|
||||
<a name='L163'></a><a href='#L163'>163</a>
|
||||
<a name='L164'></a><a href='#L164'>164</a>
|
||||
<a name='L165'></a><a href='#L165'>165</a>
|
||||
<a name='L166'></a><a href='#L166'>166</a>
|
||||
<a name='L167'></a><a href='#L167'>167</a>
|
||||
<a name='L168'></a><a href='#L168'>168</a>
|
||||
<a name='L169'></a><a href='#L169'>169</a>
|
||||
<a name='L170'></a><a href='#L170'>170</a>
|
||||
<a name='L171'></a><a href='#L171'>171</a>
|
||||
<a name='L172'></a><a href='#L172'>172</a>
|
||||
<a name='L173'></a><a href='#L173'>173</a>
|
||||
<a name='L174'></a><a href='#L174'>174</a>
|
||||
<a name='L175'></a><a href='#L175'>175</a>
|
||||
<a name='L176'></a><a href='#L176'>176</a>
|
||||
<a name='L177'></a><a href='#L177'>177</a>
|
||||
<a name='L178'></a><a href='#L178'>178</a>
|
||||
<a name='L179'></a><a href='#L179'>179</a>
|
||||
<a name='L180'></a><a href='#L180'>180</a>
|
||||
<a name='L181'></a><a href='#L181'>181</a>
|
||||
<a name='L182'></a><a href='#L182'>182</a>
|
||||
<a name='L183'></a><a href='#L183'>183</a>
|
||||
<a name='L184'></a><a href='#L184'>184</a>
|
||||
<a name='L185'></a><a href='#L185'>185</a>
|
||||
<a name='L186'></a><a href='#L186'>186</a>
|
||||
<a name='L187'></a><a href='#L187'>187</a>
|
||||
<a name='L188'></a><a href='#L188'>188</a>
|
||||
<a name='L189'></a><a href='#L189'>189</a>
|
||||
<a name='L190'></a><a href='#L190'>190</a>
|
||||
<a name='L191'></a><a href='#L191'>191</a>
|
||||
<a name='L192'></a><a href='#L192'>192</a>
|
||||
<a name='L193'></a><a href='#L193'>193</a>
|
||||
<a name='L194'></a><a href='#L194'>194</a>
|
||||
<a name='L195'></a><a href='#L195'>195</a>
|
||||
<a name='L196'></a><a href='#L196'>196</a>
|
||||
<a name='L197'></a><a href='#L197'>197</a>
|
||||
<a name='L198'></a><a href='#L198'>198</a>
|
||||
<a name='L199'></a><a href='#L199'>199</a>
|
||||
<a name='L200'></a><a href='#L200'>200</a>
|
||||
<a name='L201'></a><a href='#L201'>201</a>
|
||||
<a name='L202'></a><a href='#L202'>202</a>
|
||||
<a name='L203'></a><a href='#L203'>203</a>
|
||||
<a name='L204'></a><a href='#L204'>204</a>
|
||||
<a name='L205'></a><a href='#L205'>205</a>
|
||||
<a name='L206'></a><a href='#L206'>206</a>
|
||||
<a name='L207'></a><a href='#L207'>207</a>
|
||||
<a name='L208'></a><a href='#L208'>208</a>
|
||||
<a name='L209'></a><a href='#L209'>209</a>
|
||||
<a name='L210'></a><a href='#L210'>210</a>
|
||||
<a name='L211'></a><a href='#L211'>211</a>
|
||||
<a name='L212'></a><a href='#L212'>212</a>
|
||||
<a name='L213'></a><a href='#L213'>213</a>
|
||||
<a name='L214'></a><a href='#L214'>214</a>
|
||||
<a name='L215'></a><a href='#L215'>215</a>
|
||||
<a name='L216'></a><a href='#L216'>216</a>
|
||||
<a name='L217'></a><a href='#L217'>217</a>
|
||||
<a name='L218'></a><a href='#L218'>218</a>
|
||||
<a name='L219'></a><a href='#L219'>219</a>
|
||||
<a name='L220'></a><a href='#L220'>220</a>
|
||||
<a name='L221'></a><a href='#L221'>221</a>
|
||||
<a name='L222'></a><a href='#L222'>222</a>
|
||||
<a name='L223'></a><a href='#L223'>223</a>
|
||||
<a name='L224'></a><a href='#L224'>224</a>
|
||||
<a name='L225'></a><a href='#L225'>225</a>
|
||||
<a name='L226'></a><a href='#L226'>226</a>
|
||||
<a name='L227'></a><a href='#L227'>227</a>
|
||||
<a name='L228'></a><a href='#L228'>228</a>
|
||||
<a name='L229'></a><a href='#L229'>229</a>
|
||||
<a name='L230'></a><a href='#L230'>230</a>
|
||||
<a name='L231'></a><a href='#L231'>231</a>
|
||||
<a name='L232'></a><a href='#L232'>232</a>
|
||||
<a name='L233'></a><a href='#L233'>233</a>
|
||||
<a name='L234'></a><a href='#L234'>234</a>
|
||||
<a name='L235'></a><a href='#L235'>235</a>
|
||||
<a name='L236'></a><a href='#L236'>236</a>
|
||||
<a name='L237'></a><a href='#L237'>237</a>
|
||||
<a name='L238'></a><a href='#L238'>238</a>
|
||||
<a name='L239'></a><a href='#L239'>239</a>
|
||||
<a name='L240'></a><a href='#L240'>240</a>
|
||||
<a name='L241'></a><a href='#L241'>241</a>
|
||||
<a name='L242'></a><a href='#L242'>242</a>
|
||||
<a name='L243'></a><a href='#L243'>243</a>
|
||||
<a name='L244'></a><a href='#L244'>244</a>
|
||||
<a name='L245'></a><a href='#L245'>245</a>
|
||||
<a name='L246'></a><a href='#L246'>246</a>
|
||||
<a name='L247'></a><a href='#L247'>247</a>
|
||||
<a name='L248'></a><a href='#L248'>248</a>
|
||||
<a name='L249'></a><a href='#L249'>249</a>
|
||||
<a name='L250'></a><a href='#L250'>250</a>
|
||||
<a name='L251'></a><a href='#L251'>251</a>
|
||||
<a name='L252'></a><a href='#L252'>252</a>
|
||||
<a name='L253'></a><a href='#L253'>253</a>
|
||||
<a name='L254'></a><a href='#L254'>254</a>
|
||||
<a name='L255'></a><a href='#L255'>255</a>
|
||||
<a name='L256'></a><a href='#L256'>256</a>
|
||||
<a name='L257'></a><a href='#L257'>257</a>
|
||||
<a name='L258'></a><a href='#L258'>258</a>
|
||||
<a name='L259'></a><a href='#L259'>259</a>
|
||||
<a name='L260'></a><a href='#L260'>260</a>
|
||||
<a name='L261'></a><a href='#L261'>261</a>
|
||||
<a name='L262'></a><a href='#L262'>262</a>
|
||||
<a name='L263'></a><a href='#L263'>263</a>
|
||||
<a name='L264'></a><a href='#L264'>264</a>
|
||||
<a name='L265'></a><a href='#L265'>265</a>
|
||||
<a name='L266'></a><a href='#L266'>266</a>
|
||||
<a name='L267'></a><a href='#L267'>267</a>
|
||||
<a name='L268'></a><a href='#L268'>268</a>
|
||||
<a name='L269'></a><a href='#L269'>269</a>
|
||||
<a name='L270'></a><a href='#L270'>270</a>
|
||||
<a name='L271'></a><a href='#L271'>271</a>
|
||||
<a name='L272'></a><a href='#L272'>272</a>
|
||||
<a name='L273'></a><a href='#L273'>273</a>
|
||||
<a name='L274'></a><a href='#L274'>274</a>
|
||||
<a name='L275'></a><a href='#L275'>275</a>
|
||||
<a name='L276'></a><a href='#L276'>276</a>
|
||||
<a name='L277'></a><a href='#L277'>277</a>
|
||||
<a name='L278'></a><a href='#L278'>278</a>
|
||||
<a name='L279'></a><a href='#L279'>279</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">export type InputSpec = Record<string, ValueSpec>
|
||||
export type ValueType =
|
||||
| "text"
|
||||
| "textarea"
|
||||
| "number"
|
||||
| "color"
|
||||
| "datetime"
|
||||
| "toggle"
|
||||
| "select"
|
||||
| "multiselect"
|
||||
| "list"
|
||||
| "object"
|
||||
| "file"
|
||||
| "union"
|
||||
export type ValueSpec = ValueSpecOf<ValueType>
|
||||
/** core spec types. These types provide the metadata for performing validations */
|
||||
// prettier-ignore
|
||||
export type ValueSpecOf<T extends ValueType> =
|
||||
T extends "text" ? ValueSpecText :
|
||||
T extends "textarea" ? ValueSpecTextarea :
|
||||
T extends "number" ? ValueSpecNumber :
|
||||
T extends "color" ? ValueSpecColor :
|
||||
T extends "datetime" ? ValueSpecDatetime :
|
||||
T extends "toggle" ? ValueSpecToggle :
|
||||
T extends "select" ? ValueSpecSelect :
|
||||
T extends "multiselect" ? ValueSpecMultiselect :
|
||||
T extends "list" ? ValueSpecList :
|
||||
T extends "object" ? ValueSpecObject :
|
||||
T extends "file" ? ValueSpecFile :
|
||||
T extends "union" ? ValueSpecUnion :
|
||||
never
|
||||
|
||||
export type ValueSpecText = {
|
||||
name: string
|
||||
description: string | null
|
||||
warning: string | null
|
||||
|
||||
type: "text"
|
||||
patterns: Pattern[]
|
||||
minLength: number | null
|
||||
maxLength: number | null
|
||||
masked: boolean
|
||||
|
||||
inputmode: "text" | "email" | "tel" | "url"
|
||||
placeholder: string | null
|
||||
|
||||
required: boolean
|
||||
default: DefaultString | null
|
||||
disabled: false | string
|
||||
generate: null | RandomString
|
||||
/** Immutable means it can only be configured at the first config then never again */
|
||||
immutable: boolean
|
||||
}
|
||||
export type ValueSpecTextarea = {
|
||||
name: string
|
||||
description: string | null
|
||||
warning: string | null
|
||||
|
||||
type: "textarea"
|
||||
placeholder: string | null
|
||||
minLength: number | null
|
||||
maxLength: number | null
|
||||
required: boolean
|
||||
disabled: false | string
|
||||
/** Immutable means it can only be configured at the first config then never again */
|
||||
immutable: boolean
|
||||
}
|
||||
|
||||
export type FilePath = {
|
||||
filePath: string
|
||||
}
|
||||
export type ValueSpecNumber = {
|
||||
type: "number"
|
||||
min: number | null
|
||||
max: number | null
|
||||
integer: boolean
|
||||
step: number | null
|
||||
units: string | null
|
||||
placeholder: string | null
|
||||
name: string
|
||||
description: string | null
|
||||
warning: string | null
|
||||
required: boolean
|
||||
default: number | null
|
||||
disabled: false | string
|
||||
/** Immutable means it can only be configured at the first config then never again */
|
||||
immutable: boolean
|
||||
}
|
||||
export type ValueSpecColor = {
|
||||
name: string
|
||||
description: string | null
|
||||
warning: string | null
|
||||
|
||||
type: "color"
|
||||
required: boolean
|
||||
default: string | null
|
||||
disabled: false | string
|
||||
/** Immutable means it can only be configured at the first config then never again */
|
||||
immutable: boolean
|
||||
}
|
||||
export type ValueSpecDatetime = {
|
||||
name: string
|
||||
description: string | null
|
||||
warning: string | null
|
||||
type: "datetime"
|
||||
required: boolean
|
||||
inputmode: "date" | "time" | "datetime-local"
|
||||
min: string | null
|
||||
max: string | null
|
||||
default: string | null
|
||||
disabled: false | string
|
||||
/** Immutable means it can only be configured at the first config then never again */
|
||||
immutable: boolean
|
||||
}
|
||||
export type ValueSpecSelect = {
|
||||
values: Record<string, string>
|
||||
name: string
|
||||
description: string | null
|
||||
warning: string | null
|
||||
type: "select"
|
||||
required: boolean
|
||||
default: string | null
|
||||
/**
|
||||
* Disabled: false means that there is nothing disabled, good to modify
|
||||
* string means that this is the message displayed and the whole thing is disabled
|
||||
* string[] means that the options are disabled
|
||||
*/
|
||||
disabled: false | string | string[]
|
||||
/** Immutable means it can only be configured at the first config then never again */
|
||||
immutable: boolean
|
||||
}
|
||||
export type ValueSpecMultiselect = {
|
||||
values: Record<string, string>
|
||||
|
||||
name: string
|
||||
description: string | null
|
||||
warning: string | null
|
||||
|
||||
type: "multiselect"
|
||||
minLength: number | null
|
||||
maxLength: number | null
|
||||
/**
|
||||
* Disabled: false means that there is nothing disabled, good to modify
|
||||
* string means that this is the message displayed and the whole thing is disabled
|
||||
* string[] means that the options are disabled
|
||||
*/
|
||||
disabled: false | string | string[]
|
||||
default: string[]
|
||||
/** Immutable means it can only be configured at the first config then never again */
|
||||
immutable: boolean
|
||||
}
|
||||
export type ValueSpecToggle = {
|
||||
name: string
|
||||
description: string | null
|
||||
warning: string | null
|
||||
|
||||
type: "toggle"
|
||||
default: boolean | null
|
||||
disabled: false | string
|
||||
/** Immutable means it can only be configured at the first config then never again */
|
||||
immutable: boolean
|
||||
}
|
||||
export type ValueSpecUnion = {
|
||||
name: string
|
||||
description: string | null
|
||||
warning: string | null
|
||||
|
||||
type: "union"
|
||||
variants: Record<
|
||||
string,
|
||||
{
|
||||
name: string
|
||||
spec: InputSpec
|
||||
}
|
||||
>
|
||||
/**
|
||||
* Disabled: false means that there is nothing disabled, good to modify
|
||||
* string means that this is the message displayed and the whole thing is disabled
|
||||
* string[] means that the options are disabled
|
||||
*/
|
||||
disabled: false | string | string[]
|
||||
required: boolean
|
||||
default: string | null
|
||||
/** Immutable means it can only be configured at the first config then never again */
|
||||
immutable: boolean
|
||||
}
|
||||
export type ValueSpecFile = {
|
||||
name: string
|
||||
description: string | null
|
||||
warning: string | null
|
||||
type: "file"
|
||||
extensions: string[]
|
||||
required: boolean
|
||||
}
|
||||
export type ValueSpecObject = {
|
||||
name: string
|
||||
description: string | null
|
||||
warning: string | null
|
||||
type: "object"
|
||||
spec: InputSpec
|
||||
}
|
||||
export type ListValueSpecType = "text" | "object"
|
||||
/** represents a spec for the values of a list */
|
||||
// prettier-ignore
|
||||
export type ListValueSpecOf<T extends ListValueSpecType> =
|
||||
T extends "text" ? ListValueSpecText :
|
||||
T extends "object" ? ListValueSpecObject :
|
||||
never
|
||||
/** represents a spec for a list */
|
||||
export type ValueSpecList = ValueSpecListOf<ListValueSpecType>
|
||||
export type ValueSpecListOf<T extends ListValueSpecType> = {
|
||||
name: string
|
||||
description: string | null
|
||||
warning: string | null
|
||||
type: "list"
|
||||
spec: ListValueSpecOf<T>
|
||||
minLength: number | null
|
||||
maxLength: number | null
|
||||
disabled: false | string
|
||||
default:
|
||||
| string[]
|
||||
| DefaultString[]
|
||||
| Record<string, unknown>[]
|
||||
| readonly string[]
|
||||
| readonly DefaultString[]
|
||||
| readonly Record<string, unknown>[]
|
||||
}
|
||||
export type Pattern = {
|
||||
regex: string
|
||||
description: string
|
||||
}
|
||||
export type ListValueSpecText = {
|
||||
type: "text"
|
||||
patterns: Pattern[]
|
||||
minLength: number | null
|
||||
maxLength: number | null
|
||||
masked: boolean
|
||||
|
||||
generate: null | RandomString
|
||||
inputmode: "text" | "email" | "tel" | "url"
|
||||
placeholder: string | null
|
||||
}
|
||||
|
||||
export type ListValueSpecObject = {
|
||||
type: "object"
|
||||
/** this is a mapped type of the config object at this level, replacing the object's values with specs on those values */
|
||||
spec: InputSpec
|
||||
/** indicates whether duplicates can be permitted in the list */
|
||||
uniqueBy: UniqueBy
|
||||
/** this should be a handlebars template which can make use of the entire config which corresponds to 'spec' */
|
||||
displayAs: string | null
|
||||
}
|
||||
export type UniqueBy =
|
||||
| null
|
||||
| string
|
||||
| {
|
||||
any: readonly UniqueBy[] | UniqueBy[]
|
||||
}
|
||||
| {
|
||||
all: readonly UniqueBy[] | UniqueBy[]
|
||||
}
|
||||
export type DefaultString = string | RandomString
|
||||
export type RandomString = {
|
||||
charset: string
|
||||
len: number
|
||||
}
|
||||
// sometimes the type checker needs just a little bit of help
|
||||
export function isValueSpecListOf<S extends ListValueSpecType>(
|
||||
t: ValueSpec,
|
||||
s: S,
|
||||
): t is ValueSpecListOf<S> & { spec: ListValueSpecOf<S> } {
|
||||
return "spec" in t && t.spec.type === s
|
||||
}
|
||||
export const unionSelectKey = "unionSelectKey" as const
|
||||
export type UnionSelectKey = typeof unionSelectKey
|
||||
|
||||
export const unionValueKey = "unionValueKey" as const
|
||||
export type UnionValueKey = typeof unionValueKey
|
||||
</pre></td></tr></table></pre>
|
||||
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-07-04T02:41:23.401Z
|
||||
</div>
|
||||
<script src="../prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="../sorter.js"></script>
|
||||
<script src="../block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,176 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Code coverage report for config</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="../prettify.css" />
|
||||
<link rel="stylesheet" href="../base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(../sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1><a href="../index.html">All files</a> config</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">4/4</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">2/2</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">1/1</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">4/4</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line high"></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-col="file" data-fmt="html" data-html="true" class="file">
|
||||
File
|
||||
</th>
|
||||
<th
|
||||
data-col="pic"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
data-html="true"
|
||||
class="pic"
|
||||
></th>
|
||||
<th
|
||||
data-col="statements"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Statements
|
||||
</th>
|
||||
<th
|
||||
data-col="statements_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="branches"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Branches
|
||||
</th>
|
||||
<th
|
||||
data-col="branches_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="functions"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Functions
|
||||
</th>
|
||||
<th
|
||||
data-col="functions_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="lines"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Lines
|
||||
</th>
|
||||
<th
|
||||
data-col="lines_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="file high" data-value="configTypes.ts">
|
||||
<a href="configTypes.ts.html">configTypes.ts</a>
|
||||
</td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart">
|
||||
<div class="cover-fill cover-full" style="width: 100%"></div>
|
||||
<div class="cover-empty" style="width: 0%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="4" class="abs high">4/4</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="2" class="abs high">2/2</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="1" class="abs high">1/1</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="4" class="abs high">4/4</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-07-04T02:41:23.401Z
|
||||
</div>
|
||||
<script src="../prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="../sorter.js"></script>
|
||||
<script src="../block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,176 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Code coverage report for emverLite</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="../prettify.css" />
|
||||
<link rel="stylesheet" href="../base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(../sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1><a href="../index.html">All files</a> emverLite</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">96.99% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">129/133</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">91.89% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">34/37</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">97.56% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">40/41</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">97.65% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">125/128</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line high"></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-col="file" data-fmt="html" data-html="true" class="file">
|
||||
File
|
||||
</th>
|
||||
<th
|
||||
data-col="pic"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
data-html="true"
|
||||
class="pic"
|
||||
></th>
|
||||
<th
|
||||
data-col="statements"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Statements
|
||||
</th>
|
||||
<th
|
||||
data-col="statements_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="branches"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Branches
|
||||
</th>
|
||||
<th
|
||||
data-col="branches_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="functions"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Functions
|
||||
</th>
|
||||
<th
|
||||
data-col="functions_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="lines"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Lines
|
||||
</th>
|
||||
<th
|
||||
data-col="lines_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="file high" data-value="mod.ts">
|
||||
<a href="mod.ts.html">mod.ts</a>
|
||||
</td>
|
||||
<td data-value="96.99" class="pic high">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 96%"></div>
|
||||
<div class="cover-empty" style="width: 4%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="96.99" class="pct high">96.99%</td>
|
||||
<td data-value="133" class="abs high">129/133</td>
|
||||
<td data-value="91.89" class="pct high">91.89%</td>
|
||||
<td data-value="37" class="abs high">34/37</td>
|
||||
<td data-value="97.56" class="pct high">97.56%</td>
|
||||
<td data-value="41" class="abs high">40/41</td>
|
||||
<td data-value="97.65" class="pct high">97.65%</td>
|
||||
<td data-value="128" class="abs high">125/128</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-07-04T02:41:23.401Z
|
||||
</div>
|
||||
<script src="../prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="../sorter.js"></script>
|
||||
<script src="../block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 445 B |
@@ -1,290 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>
|
||||
Code coverage report for health/checkFns/checkPortListening.ts
|
||||
</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="../../prettify.css" />
|
||||
<link rel="stylesheet" href="../../base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(../../sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1>
|
||||
<a href="../../index.html">All files</a> /
|
||||
<a href="index.html">health/checkFns</a> checkPortListening.ts
|
||||
</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">61.11% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">11/18</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">0/7</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">42.85% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">3/7</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">61.11% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">11/18</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line medium"></div>
|
||||
<pre><table class="coverage">
|
||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
||||
<a name='L2'></a><a href='#L2'>2</a>
|
||||
<a name='L3'></a><a href='#L3'>3</a>
|
||||
<a name='L4'></a><a href='#L4'>4</a>
|
||||
<a name='L5'></a><a href='#L5'>5</a>
|
||||
<a name='L6'></a><a href='#L6'>6</a>
|
||||
<a name='L7'></a><a href='#L7'>7</a>
|
||||
<a name='L8'></a><a href='#L8'>8</a>
|
||||
<a name='L9'></a><a href='#L9'>9</a>
|
||||
<a name='L10'></a><a href='#L10'>10</a>
|
||||
<a name='L11'></a><a href='#L11'>11</a>
|
||||
<a name='L12'></a><a href='#L12'>12</a>
|
||||
<a name='L13'></a><a href='#L13'>13</a>
|
||||
<a name='L14'></a><a href='#L14'>14</a>
|
||||
<a name='L15'></a><a href='#L15'>15</a>
|
||||
<a name='L16'></a><a href='#L16'>16</a>
|
||||
<a name='L17'></a><a href='#L17'>17</a>
|
||||
<a name='L18'></a><a href='#L18'>18</a>
|
||||
<a name='L19'></a><a href='#L19'>19</a>
|
||||
<a name='L20'></a><a href='#L20'>20</a>
|
||||
<a name='L21'></a><a href='#L21'>21</a>
|
||||
<a name='L22'></a><a href='#L22'>22</a>
|
||||
<a name='L23'></a><a href='#L23'>23</a>
|
||||
<a name='L24'></a><a href='#L24'>24</a>
|
||||
<a name='L25'></a><a href='#L25'>25</a>
|
||||
<a name='L26'></a><a href='#L26'>26</a>
|
||||
<a name='L27'></a><a href='#L27'>27</a>
|
||||
<a name='L28'></a><a href='#L28'>28</a>
|
||||
<a name='L29'></a><a href='#L29'>29</a>
|
||||
<a name='L30'></a><a href='#L30'>30</a>
|
||||
<a name='L31'></a><a href='#L31'>31</a>
|
||||
<a name='L32'></a><a href='#L32'>32</a>
|
||||
<a name='L33'></a><a href='#L33'>33</a>
|
||||
<a name='L34'></a><a href='#L34'>34</a>
|
||||
<a name='L35'></a><a href='#L35'>35</a>
|
||||
<a name='L36'></a><a href='#L36'>36</a>
|
||||
<a name='L37'></a><a href='#L37'>37</a>
|
||||
<a name='L38'></a><a href='#L38'>38</a>
|
||||
<a name='L39'></a><a href='#L39'>39</a>
|
||||
<a name='L40'></a><a href='#L40'>40</a>
|
||||
<a name='L41'></a><a href='#L41'>41</a>
|
||||
<a name='L42'></a><a href='#L42'>42</a>
|
||||
<a name='L43'></a><a href='#L43'>43</a>
|
||||
<a name='L44'></a><a href='#L44'>44</a>
|
||||
<a name='L45'></a><a href='#L45'>45</a>
|
||||
<a name='L46'></a><a href='#L46'>46</a>
|
||||
<a name='L47'></a><a href='#L47'>47</a>
|
||||
<a name='L48'></a><a href='#L48'>48</a>
|
||||
<a name='L49'></a><a href='#L49'>49</a>
|
||||
<a name='L50'></a><a href='#L50'>50</a>
|
||||
<a name='L51'></a><a href='#L51'>51</a>
|
||||
<a name='L52'></a><a href='#L52'>52</a>
|
||||
<a name='L53'></a><a href='#L53'>53</a>
|
||||
<a name='L54'></a><a href='#L54'>54</a>
|
||||
<a name='L55'></a><a href='#L55'>55</a>
|
||||
<a name='L56'></a><a href='#L56'>56</a>
|
||||
<a name='L57'></a><a href='#L57'>57</a>
|
||||
<a name='L58'></a><a href='#L58'>58</a>
|
||||
<a name='L59'></a><a href='#L59'>59</a>
|
||||
<a name='L60'></a><a href='#L60'>60</a>
|
||||
<a name='L61'></a><a href='#L61'>61</a>
|
||||
<a name='L62'></a><a href='#L62'>62</a>
|
||||
<a name='L63'></a><a href='#L63'>63</a>
|
||||
<a name='L64'></a><a href='#L64'>64</a>
|
||||
<a name='L65'></a><a href='#L65'>65</a>
|
||||
<a name='L66'></a><a href='#L66'>66</a>
|
||||
<a name='L67'></a><a href='#L67'>67</a>
|
||||
<a name='L68'></a><a href='#L68'>68</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">12x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import { Effects } from "../../types"
|
||||
import { stringFromStdErrOut } from "../../util/stringFromStdErrOut"
|
||||
import { CheckResult } from "./CheckResult"
|
||||
|
||||
import { promisify } from "node:util"
|
||||
import * as CP from "node:child_process"
|
||||
|
||||
const cpExec = promisify(CP.exec)
|
||||
const cpExecFile = promisify(CP.execFile)
|
||||
export function containsAddress(x: string, port: number) {
|
||||
const readPorts = x
|
||||
.split("\n")
|
||||
.filter(Boolean)
|
||||
.splice(1)
|
||||
.map((x) => x.split(" ").filter(Boolean)[1]?.split(":")?.[1])
|
||||
.filter(Boolean)
|
||||
.map((x) => Number.parseInt(x, 16))
|
||||
.filter(Number.isFinite)
|
||||
return readPorts.indexOf(port) >= 0
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to check if a port is listening on the system.
|
||||
* Used during the health check fn or the check main fn.
|
||||
*/
|
||||
export async function <span class="fstat-no" title="function not covered" >checkPortListening(</span>
|
||||
effects: Effects,
|
||||
port: number,
|
||||
options: {
|
||||
errorMessage: string
|
||||
successMessage: string
|
||||
timeoutMessage?: string
|
||||
timeout?: number
|
||||
},
|
||||
): Promise<CheckResult> {
|
||||
<span class="cstat-no" title="statement not covered" > return Promise.race<CheckResult>([</span>
|
||||
Promise.resolve().then(<span class="fstat-no" title="function not covered" >async </span>() => {
|
||||
const hasAddress =
|
||||
<span class="cstat-no" title="statement not covered" > containsAddress(</span>
|
||||
await cpExec(`cat /proc/net/tcp`, {}).then(stringFromStdErrOut),
|
||||
port,
|
||||
) ||
|
||||
containsAddress(
|
||||
await cpExec("cat /proc/net/udp", {}).then(stringFromStdErrOut),
|
||||
port,
|
||||
)
|
||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (hasAddress) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > return { status: "success", message: options.successMessage }</span>
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > return {</span>
|
||||
status: "failure",
|
||||
message: options.errorMessage,
|
||||
}
|
||||
}),
|
||||
new Promise(<span class="fstat-no" title="function not covered" >(r</span>esolve) => {
|
||||
<span class="cstat-no" title="statement not covered" > setTimeout(</span>
|
||||
<span class="fstat-no" title="function not covered" > () =</span>>
|
||||
<span class="cstat-no" title="statement not covered" > resolve({</span>
|
||||
status: "failure",
|
||||
message:
|
||||
options.timeoutMessage || `Timeout trying to check port ${port}`,
|
||||
}),
|
||||
options.timeout ?? 1_000,
|
||||
)
|
||||
}),
|
||||
])
|
||||
}
|
||||
</pre></td></tr></table></pre>
|
||||
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-07-04T02:41:23.401Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="../../sorter.js"></script>
|
||||
<script src="../../block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,176 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Code coverage report for health/checkFns</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="../../prettify.css" />
|
||||
<link rel="stylesheet" href="../../base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(../../sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1><a href="../../index.html">All files</a> health/checkFns</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">61.11% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">11/18</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">0/7</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">42.85% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">3/7</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">61.11% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">11/18</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line medium"></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-col="file" data-fmt="html" data-html="true" class="file">
|
||||
File
|
||||
</th>
|
||||
<th
|
||||
data-col="pic"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
data-html="true"
|
||||
class="pic"
|
||||
></th>
|
||||
<th
|
||||
data-col="statements"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Statements
|
||||
</th>
|
||||
<th
|
||||
data-col="statements_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="branches"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Branches
|
||||
</th>
|
||||
<th
|
||||
data-col="branches_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="functions"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Functions
|
||||
</th>
|
||||
<th
|
||||
data-col="functions_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="lines"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Lines
|
||||
</th>
|
||||
<th
|
||||
data-col="lines_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="file medium" data-value="checkPortListening.ts">
|
||||
<a href="checkPortListening.ts.html">checkPortListening.ts</a>
|
||||
</td>
|
||||
<td data-value="61.11" class="pic medium">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 61%"></div>
|
||||
<div class="cover-empty" style="width: 39%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="61.11" class="pct medium">61.11%</td>
|
||||
<td data-value="18" class="abs medium">11/18</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="7" class="abs low">0/7</td>
|
||||
<td data-value="42.85" class="pct low">42.85%</td>
|
||||
<td data-value="7" class="abs low">3/7</td>
|
||||
<td data-value="61.11" class="pct medium">61.11%</td>
|
||||
<td data-value="18" class="abs medium">11/18</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-07-04T02:41:23.401Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="../../sorter.js"></script>
|
||||
<script src="../../block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,516 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Code coverage report for All files</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="prettify.css" />
|
||||
<link rel="stylesheet" href="base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1>All files</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">48.29% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">1329/2752</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">37.19% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">334/898</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">30.54% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">212/694</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">48.21% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">1256/2605</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line low"></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-col="file" data-fmt="html" data-html="true" class="file">
|
||||
File
|
||||
</th>
|
||||
<th
|
||||
data-col="pic"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
data-html="true"
|
||||
class="pic"
|
||||
></th>
|
||||
<th
|
||||
data-col="statements"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Statements
|
||||
</th>
|
||||
<th
|
||||
data-col="statements_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="branches"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Branches
|
||||
</th>
|
||||
<th
|
||||
data-col="branches_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="functions"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Functions
|
||||
</th>
|
||||
<th
|
||||
data-col="functions_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="lines"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Lines
|
||||
</th>
|
||||
<th
|
||||
data-col="lines_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="file low" data-value="lib">
|
||||
<a href="lib/index.html">lib</a>
|
||||
</td>
|
||||
<td data-value="40.27" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 40%"></div>
|
||||
<div class="cover-empty" style="width: 60%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="40.27" class="pct low">40.27%</td>
|
||||
<td data-value="144" class="abs low">58/144</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="11" class="abs low">0/11</td>
|
||||
<td data-value="11.76" class="pct low">11.76%</td>
|
||||
<td data-value="85" class="abs low">10/85</td>
|
||||
<td data-value="40.27" class="pct low">40.27%</td>
|
||||
<td data-value="144" class="abs low">58/144</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="lib/actions">
|
||||
<a href="lib/actions/index.html">lib/actions</a>
|
||||
</td>
|
||||
<td data-value="10.71" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 10%"></div>
|
||||
<div class="cover-empty" style="width: 90%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="10.71" class="pct low">10.71%</td>
|
||||
<td data-value="28" class="abs low">3/28</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="2" class="abs low">0/2</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="12" class="abs low">0/12</td>
|
||||
<td data-value="10.71" class="pct low">10.71%</td>
|
||||
<td data-value="28" class="abs low">3/28</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="lib/backup">
|
||||
<a href="lib/backup/index.html">lib/backup</a>
|
||||
</td>
|
||||
<td data-value="8.79" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 8%"></div>
|
||||
<div class="cover-empty" style="width: 92%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="8.79" class="pct low">8.79%</td>
|
||||
<td data-value="91" class="abs low">8/91</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="11" class="abs low">0/11</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="27" class="abs low">0/27</td>
|
||||
<td data-value="9.09" class="pct low">9.09%</td>
|
||||
<td data-value="88" class="abs low">8/88</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="lib/config">
|
||||
<a href="lib/config/index.html">lib/config</a>
|
||||
</td>
|
||||
<td data-value="46.15" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 46%"></div>
|
||||
<div class="cover-empty" style="width: 54%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="46.15" class="pct low">46.15%</td>
|
||||
<td data-value="26" class="abs low">12/26</td>
|
||||
<td data-value="25" class="pct low">25%</td>
|
||||
<td data-value="8" class="abs low">2/8</td>
|
||||
<td data-value="20" class="pct low">20%</td>
|
||||
<td data-value="5" class="abs low">1/5</td>
|
||||
<td data-value="46.15" class="pct low">46.15%</td>
|
||||
<td data-value="26" class="abs low">12/26</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="lib/config/builder">
|
||||
<a href="lib/config/builder/index.html">lib/config/builder</a>
|
||||
</td>
|
||||
<td data-value="84.61" class="pic high">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 84%"></div>
|
||||
<div class="cover-empty" style="width: 16%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="84.61" class="pct high">84.61%</td>
|
||||
<td data-value="117" class="abs high">99/117</td>
|
||||
<td data-value="55.17" class="pct medium">55.17%</td>
|
||||
<td data-value="29" class="abs medium">16/29</td>
|
||||
<td data-value="78.26" class="pct medium">78.26%</td>
|
||||
<td data-value="69" class="abs medium">54/69</td>
|
||||
<td data-value="84.34" class="pct high">84.34%</td>
|
||||
<td data-value="115" class="abs high">97/115</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="lib/dependencies">
|
||||
<a href="lib/dependencies/index.html">lib/dependencies</a>
|
||||
</td>
|
||||
<td data-value="9.67" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 9%"></div>
|
||||
<div class="cover-empty" style="width: 91%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="9.67" class="pct low">9.67%</td>
|
||||
<td data-value="93" class="abs low">9/93</td>
|
||||
<td data-value="1.78" class="pct low">1.78%</td>
|
||||
<td data-value="56" class="abs low">1/56</td>
|
||||
<td data-value="6.25" class="pct low">6.25%</td>
|
||||
<td data-value="32" class="abs low">2/32</td>
|
||||
<td data-value="10" class="pct low">10%</td>
|
||||
<td data-value="90" class="abs low">9/90</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="lib/exver">
|
||||
<a href="lib/exver/index.html">lib/exver</a>
|
||||
</td>
|
||||
<td data-value="71.73" class="pic medium">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 71%"></div>
|
||||
<div class="cover-empty" style="width: 29%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="71.73" class="pct medium">71.73%</td>
|
||||
<td data-value="994" class="abs medium">713/994</td>
|
||||
<td data-value="63.29" class="pct medium">63.29%</td>
|
||||
<td data-value="395" class="abs medium">250/395</td>
|
||||
<td data-value="71.65" class="pct medium">71.65%</td>
|
||||
<td data-value="127" class="abs medium">91/127</td>
|
||||
<td data-value="71.77" class="pct medium">71.77%</td>
|
||||
<td data-value="946" class="abs medium">679/946</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="lib/health">
|
||||
<a href="lib/health/index.html">lib/health</a>
|
||||
</td>
|
||||
<td data-value="17.24" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 17%"></div>
|
||||
<div class="cover-empty" style="width: 83%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="17.24" class="pct low">17.24%</td>
|
||||
<td data-value="29" class="abs low">5/29</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="12" class="abs low">0/12</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="6" class="abs low">0/6</td>
|
||||
<td data-value="19.23" class="pct low">19.23%</td>
|
||||
<td data-value="26" class="abs low">5/26</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="lib/health/checkFns">
|
||||
<a href="lib/health/checkFns/index.html">lib/health/checkFns</a>
|
||||
</td>
|
||||
<td data-value="53.06" class="pic medium">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 53%"></div>
|
||||
<div class="cover-empty" style="width: 47%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="53.06" class="pct medium">53.06%</td>
|
||||
<td data-value="49" class="abs medium">26/49</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="17" class="abs low">0/17</td>
|
||||
<td data-value="26.31" class="pct low">26.31%</td>
|
||||
<td data-value="19" class="abs low">5/19</td>
|
||||
<td data-value="50" class="pct medium">50%</td>
|
||||
<td data-value="44" class="abs medium">22/44</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="lib/inits">
|
||||
<a href="lib/inits/index.html">lib/inits</a>
|
||||
</td>
|
||||
<td data-value="20.68" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 20%"></div>
|
||||
<div class="cover-empty" style="width: 80%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="20.68" class="pct low">20.68%</td>
|
||||
<td data-value="29" class="abs low">6/29</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="6" class="abs low">0/6</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="11" class="abs low">0/11</td>
|
||||
<td data-value="20.68" class="pct low">20.68%</td>
|
||||
<td data-value="29" class="abs low">6/29</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="lib/interfaces">
|
||||
<a href="lib/interfaces/index.html">lib/interfaces</a>
|
||||
</td>
|
||||
<td data-value="22.44" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 22%"></div>
|
||||
<div class="cover-empty" style="width: 78%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="22.44" class="pct low">22.44%</td>
|
||||
<td data-value="49" class="abs low">11/49</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="24" class="abs low">0/24</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="13" class="abs low">0/13</td>
|
||||
<td data-value="22.22" class="pct low">22.22%</td>
|
||||
<td data-value="45" class="abs low">10/45</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="lib/mainFn">
|
||||
<a href="lib/mainFn/index.html">lib/mainFn</a>
|
||||
</td>
|
||||
<td data-value="15.78" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 15%"></div>
|
||||
<div class="cover-empty" style="width: 85%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="15.78" class="pct low">15.78%</td>
|
||||
<td data-value="228" class="abs low">36/228</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="38" class="abs low">0/38</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="77" class="abs low">0/77</td>
|
||||
<td data-value="15.16" class="pct low">15.16%</td>
|
||||
<td data-value="211" class="abs low">32/211</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="lib/manifest">
|
||||
<a href="lib/manifest/index.html">lib/manifest</a>
|
||||
</td>
|
||||
<td data-value="25" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 25%"></div>
|
||||
<div class="cover-empty" style="width: 75%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="25" class="pct low">25%</td>
|
||||
<td data-value="16" class="abs low">4/16</td>
|
||||
<td data-value="65.51" class="pct medium">65.51%</td>
|
||||
<td data-value="29" class="abs medium">19/29</td>
|
||||
<td data-value="20" class="pct low">20%</td>
|
||||
<td data-value="5" class="abs low">1/5</td>
|
||||
<td data-value="26.66" class="pct low">26.66%</td>
|
||||
<td data-value="15" class="abs low">4/15</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="lib/store">
|
||||
<a href="lib/store/index.html">lib/store</a>
|
||||
</td>
|
||||
<td data-value="35.71" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 35%"></div>
|
||||
<div class="cover-empty" style="width: 65%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="35.71" class="pct low">35.71%</td>
|
||||
<td data-value="28" class="abs low">10/28</td>
|
||||
<td data-value="20" class="pct low">20%</td>
|
||||
<td data-value="5" class="abs low">1/5</td>
|
||||
<td data-value="10" class="pct low">10%</td>
|
||||
<td data-value="10" class="abs low">1/10</td>
|
||||
<td data-value="32" class="pct low">32%</td>
|
||||
<td data-value="25" class="abs low">8/25</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="lib/test">
|
||||
<a href="lib/test/index.html">lib/test</a>
|
||||
</td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart">
|
||||
<div class="cover-fill cover-full" style="width: 100%"></div>
|
||||
<div class="cover-empty" style="width: 0%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="9" class="abs high">9/9</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="0" class="abs high">0/0</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="0" class="abs high">0/0</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="9" class="abs high">9/9</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="lib/trigger">
|
||||
<a href="lib/trigger/index.html">lib/trigger</a>
|
||||
</td>
|
||||
<td data-value="30.61" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 30%"></div>
|
||||
<div class="cover-empty" style="width: 70%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="30.61" class="pct low">30.61%</td>
|
||||
<td data-value="49" class="abs low">15/49</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="5" class="abs low">0/5</td>
|
||||
<td data-value="40" class="pct low">40%</td>
|
||||
<td data-value="10" class="abs low">4/10</td>
|
||||
<td data-value="26.66" class="pct low">26.66%</td>
|
||||
<td data-value="45" class="abs low">12/45</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="lib/util">
|
||||
<a href="lib/util/index.html">lib/util</a>
|
||||
</td>
|
||||
<td data-value="36.83" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 36%"></div>
|
||||
<div class="cover-empty" style="width: 64%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="36.83" class="pct low">36.83%</td>
|
||||
<td data-value="695" class="abs low">256/695</td>
|
||||
<td data-value="12.2" class="pct low">12.2%</td>
|
||||
<td data-value="213" class="abs low">26/213</td>
|
||||
<td data-value="17.96" class="pct low">17.96%</td>
|
||||
<td data-value="167" class="abs low">30/167</td>
|
||||
<td data-value="36.29" class="pct low">36.29%</td>
|
||||
<td data-value="642" class="abs low">233/642</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="lib/version">
|
||||
<a href="lib/version/index.html">lib/version</a>
|
||||
</td>
|
||||
<td data-value="62.82" class="pic medium">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 62%"></div>
|
||||
<div class="cover-empty" style="width: 38%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="62.82" class="pct medium">62.82%</td>
|
||||
<td data-value="78" class="abs medium">49/78</td>
|
||||
<td data-value="51.35" class="pct medium">51.35%</td>
|
||||
<td data-value="37" class="abs medium">19/37</td>
|
||||
<td data-value="68.42" class="pct medium">68.42%</td>
|
||||
<td data-value="19" class="abs medium">13/19</td>
|
||||
<td data-value="63.63" class="pct medium">63.63%</td>
|
||||
<td data-value="77" class="abs medium">49/77</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-08-28T21:46:45.516Z
|
||||
</div>
|
||||
<script src="prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="sorter.js"></script>
|
||||
<script src="block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,669 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Code coverage report for interfaces/Host.ts</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="../prettify.css" />
|
||||
<link rel="stylesheet" href="../base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(../sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1>
|
||||
<a href="../index.html">All files</a> /
|
||||
<a href="index.html">interfaces</a> Host.ts
|
||||
</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">22.22% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">6/27</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">0/18</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">0/7</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">24% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">6/25</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line low"></div>
|
||||
<pre><table class="coverage">
|
||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
||||
<a name='L2'></a><a href='#L2'>2</a>
|
||||
<a name='L3'></a><a href='#L3'>3</a>
|
||||
<a name='L4'></a><a href='#L4'>4</a>
|
||||
<a name='L5'></a><a href='#L5'>5</a>
|
||||
<a name='L6'></a><a href='#L6'>6</a>
|
||||
<a name='L7'></a><a href='#L7'>7</a>
|
||||
<a name='L8'></a><a href='#L8'>8</a>
|
||||
<a name='L9'></a><a href='#L9'>9</a>
|
||||
<a name='L10'></a><a href='#L10'>10</a>
|
||||
<a name='L11'></a><a href='#L11'>11</a>
|
||||
<a name='L12'></a><a href='#L12'>12</a>
|
||||
<a name='L13'></a><a href='#L13'>13</a>
|
||||
<a name='L14'></a><a href='#L14'>14</a>
|
||||
<a name='L15'></a><a href='#L15'>15</a>
|
||||
<a name='L16'></a><a href='#L16'>16</a>
|
||||
<a name='L17'></a><a href='#L17'>17</a>
|
||||
<a name='L18'></a><a href='#L18'>18</a>
|
||||
<a name='L19'></a><a href='#L19'>19</a>
|
||||
<a name='L20'></a><a href='#L20'>20</a>
|
||||
<a name='L21'></a><a href='#L21'>21</a>
|
||||
<a name='L22'></a><a href='#L22'>22</a>
|
||||
<a name='L23'></a><a href='#L23'>23</a>
|
||||
<a name='L24'></a><a href='#L24'>24</a>
|
||||
<a name='L25'></a><a href='#L25'>25</a>
|
||||
<a name='L26'></a><a href='#L26'>26</a>
|
||||
<a name='L27'></a><a href='#L27'>27</a>
|
||||
<a name='L28'></a><a href='#L28'>28</a>
|
||||
<a name='L29'></a><a href='#L29'>29</a>
|
||||
<a name='L30'></a><a href='#L30'>30</a>
|
||||
<a name='L31'></a><a href='#L31'>31</a>
|
||||
<a name='L32'></a><a href='#L32'>32</a>
|
||||
<a name='L33'></a><a href='#L33'>33</a>
|
||||
<a name='L34'></a><a href='#L34'>34</a>
|
||||
<a name='L35'></a><a href='#L35'>35</a>
|
||||
<a name='L36'></a><a href='#L36'>36</a>
|
||||
<a name='L37'></a><a href='#L37'>37</a>
|
||||
<a name='L38'></a><a href='#L38'>38</a>
|
||||
<a name='L39'></a><a href='#L39'>39</a>
|
||||
<a name='L40'></a><a href='#L40'>40</a>
|
||||
<a name='L41'></a><a href='#L41'>41</a>
|
||||
<a name='L42'></a><a href='#L42'>42</a>
|
||||
<a name='L43'></a><a href='#L43'>43</a>
|
||||
<a name='L44'></a><a href='#L44'>44</a>
|
||||
<a name='L45'></a><a href='#L45'>45</a>
|
||||
<a name='L46'></a><a href='#L46'>46</a>
|
||||
<a name='L47'></a><a href='#L47'>47</a>
|
||||
<a name='L48'></a><a href='#L48'>48</a>
|
||||
<a name='L49'></a><a href='#L49'>49</a>
|
||||
<a name='L50'></a><a href='#L50'>50</a>
|
||||
<a name='L51'></a><a href='#L51'>51</a>
|
||||
<a name='L52'></a><a href='#L52'>52</a>
|
||||
<a name='L53'></a><a href='#L53'>53</a>
|
||||
<a name='L54'></a><a href='#L54'>54</a>
|
||||
<a name='L55'></a><a href='#L55'>55</a>
|
||||
<a name='L56'></a><a href='#L56'>56</a>
|
||||
<a name='L57'></a><a href='#L57'>57</a>
|
||||
<a name='L58'></a><a href='#L58'>58</a>
|
||||
<a name='L59'></a><a href='#L59'>59</a>
|
||||
<a name='L60'></a><a href='#L60'>60</a>
|
||||
<a name='L61'></a><a href='#L61'>61</a>
|
||||
<a name='L62'></a><a href='#L62'>62</a>
|
||||
<a name='L63'></a><a href='#L63'>63</a>
|
||||
<a name='L64'></a><a href='#L64'>64</a>
|
||||
<a name='L65'></a><a href='#L65'>65</a>
|
||||
<a name='L66'></a><a href='#L66'>66</a>
|
||||
<a name='L67'></a><a href='#L67'>67</a>
|
||||
<a name='L68'></a><a href='#L68'>68</a>
|
||||
<a name='L69'></a><a href='#L69'>69</a>
|
||||
<a name='L70'></a><a href='#L70'>70</a>
|
||||
<a name='L71'></a><a href='#L71'>71</a>
|
||||
<a name='L72'></a><a href='#L72'>72</a>
|
||||
<a name='L73'></a><a href='#L73'>73</a>
|
||||
<a name='L74'></a><a href='#L74'>74</a>
|
||||
<a name='L75'></a><a href='#L75'>75</a>
|
||||
<a name='L76'></a><a href='#L76'>76</a>
|
||||
<a name='L77'></a><a href='#L77'>77</a>
|
||||
<a name='L78'></a><a href='#L78'>78</a>
|
||||
<a name='L79'></a><a href='#L79'>79</a>
|
||||
<a name='L80'></a><a href='#L80'>80</a>
|
||||
<a name='L81'></a><a href='#L81'>81</a>
|
||||
<a name='L82'></a><a href='#L82'>82</a>
|
||||
<a name='L83'></a><a href='#L83'>83</a>
|
||||
<a name='L84'></a><a href='#L84'>84</a>
|
||||
<a name='L85'></a><a href='#L85'>85</a>
|
||||
<a name='L86'></a><a href='#L86'>86</a>
|
||||
<a name='L87'></a><a href='#L87'>87</a>
|
||||
<a name='L88'></a><a href='#L88'>88</a>
|
||||
<a name='L89'></a><a href='#L89'>89</a>
|
||||
<a name='L90'></a><a href='#L90'>90</a>
|
||||
<a name='L91'></a><a href='#L91'>91</a>
|
||||
<a name='L92'></a><a href='#L92'>92</a>
|
||||
<a name='L93'></a><a href='#L93'>93</a>
|
||||
<a name='L94'></a><a href='#L94'>94</a>
|
||||
<a name='L95'></a><a href='#L95'>95</a>
|
||||
<a name='L96'></a><a href='#L96'>96</a>
|
||||
<a name='L97'></a><a href='#L97'>97</a>
|
||||
<a name='L98'></a><a href='#L98'>98</a>
|
||||
<a name='L99'></a><a href='#L99'>99</a>
|
||||
<a name='L100'></a><a href='#L100'>100</a>
|
||||
<a name='L101'></a><a href='#L101'>101</a>
|
||||
<a name='L102'></a><a href='#L102'>102</a>
|
||||
<a name='L103'></a><a href='#L103'>103</a>
|
||||
<a name='L104'></a><a href='#L104'>104</a>
|
||||
<a name='L105'></a><a href='#L105'>105</a>
|
||||
<a name='L106'></a><a href='#L106'>106</a>
|
||||
<a name='L107'></a><a href='#L107'>107</a>
|
||||
<a name='L108'></a><a href='#L108'>108</a>
|
||||
<a name='L109'></a><a href='#L109'>109</a>
|
||||
<a name='L110'></a><a href='#L110'>110</a>
|
||||
<a name='L111'></a><a href='#L111'>111</a>
|
||||
<a name='L112'></a><a href='#L112'>112</a>
|
||||
<a name='L113'></a><a href='#L113'>113</a>
|
||||
<a name='L114'></a><a href='#L114'>114</a>
|
||||
<a name='L115'></a><a href='#L115'>115</a>
|
||||
<a name='L116'></a><a href='#L116'>116</a>
|
||||
<a name='L117'></a><a href='#L117'>117</a>
|
||||
<a name='L118'></a><a href='#L118'>118</a>
|
||||
<a name='L119'></a><a href='#L119'>119</a>
|
||||
<a name='L120'></a><a href='#L120'>120</a>
|
||||
<a name='L121'></a><a href='#L121'>121</a>
|
||||
<a name='L122'></a><a href='#L122'>122</a>
|
||||
<a name='L123'></a><a href='#L123'>123</a>
|
||||
<a name='L124'></a><a href='#L124'>124</a>
|
||||
<a name='L125'></a><a href='#L125'>125</a>
|
||||
<a name='L126'></a><a href='#L126'>126</a>
|
||||
<a name='L127'></a><a href='#L127'>127</a>
|
||||
<a name='L128'></a><a href='#L128'>128</a>
|
||||
<a name='L129'></a><a href='#L129'>129</a>
|
||||
<a name='L130'></a><a href='#L130'>130</a>
|
||||
<a name='L131'></a><a href='#L131'>131</a>
|
||||
<a name='L132'></a><a href='#L132'>132</a>
|
||||
<a name='L133'></a><a href='#L133'>133</a>
|
||||
<a name='L134'></a><a href='#L134'>134</a>
|
||||
<a name='L135'></a><a href='#L135'>135</a>
|
||||
<a name='L136'></a><a href='#L136'>136</a>
|
||||
<a name='L137'></a><a href='#L137'>137</a>
|
||||
<a name='L138'></a><a href='#L138'>138</a>
|
||||
<a name='L139'></a><a href='#L139'>139</a>
|
||||
<a name='L140'></a><a href='#L140'>140</a>
|
||||
<a name='L141'></a><a href='#L141'>141</a>
|
||||
<a name='L142'></a><a href='#L142'>142</a>
|
||||
<a name='L143'></a><a href='#L143'>143</a>
|
||||
<a name='L144'></a><a href='#L144'>144</a>
|
||||
<a name='L145'></a><a href='#L145'>145</a>
|
||||
<a name='L146'></a><a href='#L146'>146</a>
|
||||
<a name='L147'></a><a href='#L147'>147</a>
|
||||
<a name='L148'></a><a href='#L148'>148</a>
|
||||
<a name='L149'></a><a href='#L149'>149</a>
|
||||
<a name='L150'></a><a href='#L150'>150</a>
|
||||
<a name='L151'></a><a href='#L151'>151</a>
|
||||
<a name='L152'></a><a href='#L152'>152</a>
|
||||
<a name='L153'></a><a href='#L153'>153</a>
|
||||
<a name='L154'></a><a href='#L154'>154</a>
|
||||
<a name='L155'></a><a href='#L155'>155</a>
|
||||
<a name='L156'></a><a href='#L156'>156</a>
|
||||
<a name='L157'></a><a href='#L157'>157</a>
|
||||
<a name='L158'></a><a href='#L158'>158</a>
|
||||
<a name='L159'></a><a href='#L159'>159</a>
|
||||
<a name='L160'></a><a href='#L160'>160</a>
|
||||
<a name='L161'></a><a href='#L161'>161</a>
|
||||
<a name='L162'></a><a href='#L162'>162</a>
|
||||
<a name='L163'></a><a href='#L163'>163</a>
|
||||
<a name='L164'></a><a href='#L164'>164</a>
|
||||
<a name='L165'></a><a href='#L165'>165</a>
|
||||
<a name='L166'></a><a href='#L166'>166</a>
|
||||
<a name='L167'></a><a href='#L167'>167</a>
|
||||
<a name='L168'></a><a href='#L168'>168</a>
|
||||
<a name='L169'></a><a href='#L169'>169</a>
|
||||
<a name='L170'></a><a href='#L170'>170</a>
|
||||
<a name='L171'></a><a href='#L171'>171</a>
|
||||
<a name='L172'></a><a href='#L172'>172</a>
|
||||
<a name='L173'></a><a href='#L173'>173</a>
|
||||
<a name='L174'></a><a href='#L174'>174</a>
|
||||
<a name='L175'></a><a href='#L175'>175</a>
|
||||
<a name='L176'></a><a href='#L176'>176</a>
|
||||
<a name='L177'></a><a href='#L177'>177</a>
|
||||
<a name='L178'></a><a href='#L178'>178</a>
|
||||
<a name='L179'></a><a href='#L179'>179</a>
|
||||
<a name='L180'></a><a href='#L180'>180</a>
|
||||
<a name='L181'></a><a href='#L181'>181</a>
|
||||
<a name='L182'></a><a href='#L182'>182</a>
|
||||
<a name='L183'></a><a href='#L183'>183</a>
|
||||
<a name='L184'></a><a href='#L184'>184</a>
|
||||
<a name='L185'></a><a href='#L185'>185</a>
|
||||
<a name='L186'></a><a href='#L186'>186</a>
|
||||
<a name='L187'></a><a href='#L187'>187</a>
|
||||
<a name='L188'></a><a href='#L188'>188</a>
|
||||
<a name='L189'></a><a href='#L189'>189</a>
|
||||
<a name='L190'></a><a href='#L190'>190</a>
|
||||
<a name='L191'></a><a href='#L191'>191</a>
|
||||
<a name='L192'></a><a href='#L192'>192</a>
|
||||
<a name='L193'></a><a href='#L193'>193</a>
|
||||
<a name='L194'></a><a href='#L194'>194</a>
|
||||
<a name='L195'></a><a href='#L195'>195</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import { number, object, string } from "ts-matches"
|
||||
import { Effects } from "../types"
|
||||
import { Origin } from "./Origin"
|
||||
import { AddSslOptions, BindParams } from ".././osBindings"
|
||||
import { Security } from ".././osBindings"
|
||||
import { BindOptions } from ".././osBindings"
|
||||
import { AlpnInfo } from ".././osBindings"
|
||||
|
||||
export { AddSslOptions, Security, BindOptions }
|
||||
|
||||
export const knownProtocols = {
|
||||
http: {
|
||||
secure: null,
|
||||
defaultPort: 80,
|
||||
withSsl: "https",
|
||||
alpn: { specified: ["http/1.1"] } as AlpnInfo,
|
||||
},
|
||||
https: {
|
||||
secure: { ssl: true },
|
||||
defaultPort: 443,
|
||||
},
|
||||
ws: {
|
||||
secure: null,
|
||||
defaultPort: 80,
|
||||
withSsl: "wss",
|
||||
alpn: { specified: ["http/1.1"] } as AlpnInfo,
|
||||
},
|
||||
wss: {
|
||||
secure: { ssl: true },
|
||||
defaultPort: 443,
|
||||
},
|
||||
ssh: {
|
||||
secure: { ssl: false },
|
||||
defaultPort: 22,
|
||||
},
|
||||
bitcoin: {
|
||||
secure: { ssl: false },
|
||||
defaultPort: 8333,
|
||||
},
|
||||
lightning: {
|
||||
secure: { ssl: true },
|
||||
defaultPort: 9735,
|
||||
},
|
||||
grpc: {
|
||||
secure: { ssl: true },
|
||||
defaultPort: 50051,
|
||||
},
|
||||
dns: {
|
||||
secure: { ssl: false },
|
||||
defaultPort: 53,
|
||||
},
|
||||
} as const
|
||||
|
||||
export type Scheme = string | null
|
||||
|
||||
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 BindOptionsByKnownProtocol =
|
||||
| {
|
||||
protocol: ProtocolsWithSslVariants
|
||||
preferredExternalPort?: number
|
||||
addSsl?: Partial<AddSslOptions>
|
||||
}
|
||||
| {
|
||||
protocol: NotProtocolsWithSslVariants
|
||||
preferredExternalPort?: number
|
||||
addSsl?: AddSslOptions
|
||||
}
|
||||
export type BindOptionsByProtocol = BindOptionsByKnownProtocol | BindOptions
|
||||
|
||||
export type HostKind = BindParams["kind"]
|
||||
|
||||
const hasStringProtocol = object({
|
||||
protocol: string,
|
||||
}).test
|
||||
|
||||
export class Host {
|
||||
<span class="fstat-no" title="function not covered" > constructor(</span>
|
||||
readonly <span class="cstat-no" title="statement not covered" >options: {</span>
|
||||
effects: Effects
|
||||
kind: HostKind
|
||||
id: string
|
||||
},
|
||||
) {}
|
||||
|
||||
<span class="fstat-no" title="function not covered" > async </span>bindPort(
|
||||
internalPort: number,
|
||||
options: BindOptionsByProtocol,
|
||||
): Promise<Origin<this>> {
|
||||
<span class="cstat-no" title="statement not covered" > if (hasStringProtocol(options)) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > return await this.bindPortForKnown(options, internalPort)</span>
|
||||
} else {
|
||||
<span class="cstat-no" title="statement not covered" > return await this.bindPortForUnknown(internalPort, options)</span>
|
||||
}
|
||||
}
|
||||
|
||||
private <span class="fstat-no" title="function not covered" >async </span>bindPortForUnknown(
|
||||
internalPort: number,
|
||||
options: {
|
||||
preferredExternalPort: number
|
||||
addSsl: AddSslOptions | null
|
||||
secure: { ssl: boolean } | null
|
||||
},
|
||||
) {
|
||||
const binderOptions = <span class="cstat-no" title="statement not covered" >{</span>
|
||||
kind: this.options.kind,
|
||||
id: this.options.id,
|
||||
internalPort,
|
||||
...options,
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > await this.options.effects.bind(binderOptions)</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return new Origin(this, internalPort, null, null)</span>
|
||||
}
|
||||
|
||||
private <span class="fstat-no" title="function not covered" >async </span>bindPortForKnown(
|
||||
options: BindOptionsByKnownProtocol,
|
||||
internalPort: number,
|
||||
) {
|
||||
const protoInfo = <span class="cstat-no" title="statement not covered" >knownProtocols[options.protocol]</span>
|
||||
const preferredExternalPort =
|
||||
<span class="cstat-no" title="statement not covered" > options.preferredExternalPort ||</span>
|
||||
knownProtocols[options.protocol].defaultPort
|
||||
const sslProto = <span class="cstat-no" title="statement not covered" >this.getSslProto(options, protoInfo)</span>
|
||||
const addSsl =
|
||||
<span class="cstat-no" title="statement not covered" > sslProto && "alpn" in protoInfo</span>
|
||||
? {
|
||||
// addXForwardedHeaders: null,
|
||||
preferredExternalPort: knownProtocols[sslProto].defaultPort,
|
||||
scheme: sslProto,
|
||||
alpn: protoInfo.alpn,
|
||||
...("addSsl" in options ? options.addSsl : null),
|
||||
}
|
||||
: null
|
||||
|
||||
const secure: Security | null = <span class="cstat-no" title="statement not covered" >!protoInfo.secure ? null : { ssl: false }</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > await this.options.effects.bind({</span>
|
||||
kind: this.options.kind,
|
||||
id: this.options.id,
|
||||
internalPort,
|
||||
preferredExternalPort,
|
||||
addSsl,
|
||||
secure,
|
||||
})
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return new Origin(this, internalPort, options.protocol, sslProto)</span>
|
||||
}
|
||||
|
||||
private <span class="fstat-no" title="function not covered" >getSslProto(</span>
|
||||
options: BindOptionsByKnownProtocol,
|
||||
protoInfo: KnownProtocols[keyof KnownProtocols],
|
||||
) {
|
||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (inObject("noAddSsl", options) && options.noAddSsl) <span class="cstat-no" title="statement not covered" >return null</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if ("withSsl" in protoInfo && protoInfo.withSsl) <span class="cstat-no" title="statement not covered" >return protoInfo.withSsl</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > return null</span>
|
||||
}
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >inObject<</span>Key extends string>(
|
||||
key: Key,
|
||||
obj: any,
|
||||
): obj is { [K in Key]: unknown } {
|
||||
<span class="cstat-no" title="statement not covered" > return key in obj</span>
|
||||
}
|
||||
|
||||
// export class StaticHost extends Host {
|
||||
// constructor(options: { effects: Effects; id: string }) {
|
||||
// super({ ...options, kind: "static" })
|
||||
// }
|
||||
// }
|
||||
|
||||
// export class SingleHost extends Host {
|
||||
// constructor(options: { effects: Effects; id: string }) {
|
||||
// super({ ...options, kind: "single" })
|
||||
// }
|
||||
// }
|
||||
|
||||
export class MultiHost extends Host {
|
||||
<span class="fstat-no" title="function not covered" > constructor(o</span>ptions: { effects: Effects; id: string }) {
|
||||
<span class="cstat-no" title="statement not covered" > super({ ...options, kind: "multi" })</span>
|
||||
}
|
||||
}
|
||||
</pre></td></tr></table></pre>
|
||||
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-07-04T02:41:23.401Z
|
||||
</div>
|
||||
<script src="../prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="../sorter.js"></script>
|
||||
<script src="../block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,357 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Code coverage report for interfaces/Origin.ts</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="../prettify.css" />
|
||||
<link rel="stylesheet" href="../base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(../sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1>
|
||||
<a href="../index.html">All files</a> /
|
||||
<a href="index.html">interfaces</a> Origin.ts
|
||||
</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">6.25% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">1/16</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">0/6</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">0/4</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">6.25% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">1/16</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line low"></div>
|
||||
<pre><table class="coverage">
|
||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
||||
<a name='L2'></a><a href='#L2'>2</a>
|
||||
<a name='L3'></a><a href='#L3'>3</a>
|
||||
<a name='L4'></a><a href='#L4'>4</a>
|
||||
<a name='L5'></a><a href='#L5'>5</a>
|
||||
<a name='L6'></a><a href='#L6'>6</a>
|
||||
<a name='L7'></a><a href='#L7'>7</a>
|
||||
<a name='L8'></a><a href='#L8'>8</a>
|
||||
<a name='L9'></a><a href='#L9'>9</a>
|
||||
<a name='L10'></a><a href='#L10'>10</a>
|
||||
<a name='L11'></a><a href='#L11'>11</a>
|
||||
<a name='L12'></a><a href='#L12'>12</a>
|
||||
<a name='L13'></a><a href='#L13'>13</a>
|
||||
<a name='L14'></a><a href='#L14'>14</a>
|
||||
<a name='L15'></a><a href='#L15'>15</a>
|
||||
<a name='L16'></a><a href='#L16'>16</a>
|
||||
<a name='L17'></a><a href='#L17'>17</a>
|
||||
<a name='L18'></a><a href='#L18'>18</a>
|
||||
<a name='L19'></a><a href='#L19'>19</a>
|
||||
<a name='L20'></a><a href='#L20'>20</a>
|
||||
<a name='L21'></a><a href='#L21'>21</a>
|
||||
<a name='L22'></a><a href='#L22'>22</a>
|
||||
<a name='L23'></a><a href='#L23'>23</a>
|
||||
<a name='L24'></a><a href='#L24'>24</a>
|
||||
<a name='L25'></a><a href='#L25'>25</a>
|
||||
<a name='L26'></a><a href='#L26'>26</a>
|
||||
<a name='L27'></a><a href='#L27'>27</a>
|
||||
<a name='L28'></a><a href='#L28'>28</a>
|
||||
<a name='L29'></a><a href='#L29'>29</a>
|
||||
<a name='L30'></a><a href='#L30'>30</a>
|
||||
<a name='L31'></a><a href='#L31'>31</a>
|
||||
<a name='L32'></a><a href='#L32'>32</a>
|
||||
<a name='L33'></a><a href='#L33'>33</a>
|
||||
<a name='L34'></a><a href='#L34'>34</a>
|
||||
<a name='L35'></a><a href='#L35'>35</a>
|
||||
<a name='L36'></a><a href='#L36'>36</a>
|
||||
<a name='L37'></a><a href='#L37'>37</a>
|
||||
<a name='L38'></a><a href='#L38'>38</a>
|
||||
<a name='L39'></a><a href='#L39'>39</a>
|
||||
<a name='L40'></a><a href='#L40'>40</a>
|
||||
<a name='L41'></a><a href='#L41'>41</a>
|
||||
<a name='L42'></a><a href='#L42'>42</a>
|
||||
<a name='L43'></a><a href='#L43'>43</a>
|
||||
<a name='L44'></a><a href='#L44'>44</a>
|
||||
<a name='L45'></a><a href='#L45'>45</a>
|
||||
<a name='L46'></a><a href='#L46'>46</a>
|
||||
<a name='L47'></a><a href='#L47'>47</a>
|
||||
<a name='L48'></a><a href='#L48'>48</a>
|
||||
<a name='L49'></a><a href='#L49'>49</a>
|
||||
<a name='L50'></a><a href='#L50'>50</a>
|
||||
<a name='L51'></a><a href='#L51'>51</a>
|
||||
<a name='L52'></a><a href='#L52'>52</a>
|
||||
<a name='L53'></a><a href='#L53'>53</a>
|
||||
<a name='L54'></a><a href='#L54'>54</a>
|
||||
<a name='L55'></a><a href='#L55'>55</a>
|
||||
<a name='L56'></a><a href='#L56'>56</a>
|
||||
<a name='L57'></a><a href='#L57'>57</a>
|
||||
<a name='L58'></a><a href='#L58'>58</a>
|
||||
<a name='L59'></a><a href='#L59'>59</a>
|
||||
<a name='L60'></a><a href='#L60'>60</a>
|
||||
<a name='L61'></a><a href='#L61'>61</a>
|
||||
<a name='L62'></a><a href='#L62'>62</a>
|
||||
<a name='L63'></a><a href='#L63'>63</a>
|
||||
<a name='L64'></a><a href='#L64'>64</a>
|
||||
<a name='L65'></a><a href='#L65'>65</a>
|
||||
<a name='L66'></a><a href='#L66'>66</a>
|
||||
<a name='L67'></a><a href='#L67'>67</a>
|
||||
<a name='L68'></a><a href='#L68'>68</a>
|
||||
<a name='L69'></a><a href='#L69'>69</a>
|
||||
<a name='L70'></a><a href='#L70'>70</a>
|
||||
<a name='L71'></a><a href='#L71'>71</a>
|
||||
<a name='L72'></a><a href='#L72'>72</a>
|
||||
<a name='L73'></a><a href='#L73'>73</a>
|
||||
<a name='L74'></a><a href='#L74'>74</a>
|
||||
<a name='L75'></a><a href='#L75'>75</a>
|
||||
<a name='L76'></a><a href='#L76'>76</a>
|
||||
<a name='L77'></a><a href='#L77'>77</a>
|
||||
<a name='L78'></a><a href='#L78'>78</a>
|
||||
<a name='L79'></a><a href='#L79'>79</a>
|
||||
<a name='L80'></a><a href='#L80'>80</a>
|
||||
<a name='L81'></a><a href='#L81'>81</a>
|
||||
<a name='L82'></a><a href='#L82'>82</a>
|
||||
<a name='L83'></a><a href='#L83'>83</a>
|
||||
<a name='L84'></a><a href='#L84'>84</a>
|
||||
<a name='L85'></a><a href='#L85'>85</a>
|
||||
<a name='L86'></a><a href='#L86'>86</a>
|
||||
<a name='L87'></a><a href='#L87'>87</a>
|
||||
<a name='L88'></a><a href='#L88'>88</a>
|
||||
<a name='L89'></a><a href='#L89'>89</a>
|
||||
<a name='L90'></a><a href='#L90'>90</a>
|
||||
<a name='L91'></a><a href='#L91'>91</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import { AddressInfo } from "../types"
|
||||
import { AddressReceipt } from "./AddressReceipt"
|
||||
import { Host, BindOptions, Scheme } from "./Host"
|
||||
import { ServiceInterfaceBuilder } from "./ServiceInterfaceBuilder"
|
||||
|
||||
export class Origin<T extends Host> {
|
||||
<span class="fstat-no" title="function not covered" > constructor(</span>
|
||||
readonly <span class="cstat-no" title="statement not covered" >host: T,</span>
|
||||
readonly <span class="cstat-no" title="statement not covered" >internalPort: number,</span>
|
||||
readonly <span class="cstat-no" title="statement not covered" >scheme: string | null,</span>
|
||||
readonly <span class="cstat-no" title="statement not covered" >sslScheme: string | null,</span>
|
||||
) {}
|
||||
|
||||
<span class="fstat-no" title="function not covered" > build(</span>{ username, path, search, schemeOverride }: BuildOptions): AddressInfo {
|
||||
const qpEntries = <span class="cstat-no" title="statement not covered" >Object.entries(search)</span>
|
||||
.map(
|
||||
<span class="fstat-no" title="function not covered" > ([</span>key, val]) => <span class="cstat-no" title="statement not covered" >`${encodeURIComponent(key)}=${encodeURIComponent(val)}`,</span>
|
||||
)
|
||||
.join("&")
|
||||
|
||||
const qp = <span class="cstat-no" title="statement not covered" >qpEntries.length ? `?${qpEntries}` : ""</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return {</span>
|
||||
hostId: this.host.options.id,
|
||||
internalPort: this.internalPort,
|
||||
scheme: schemeOverride ? schemeOverride.noSsl : this.scheme,
|
||||
sslScheme: schemeOverride ? schemeOverride.ssl : this.sslScheme,
|
||||
suffix: `${path}${qp}`,
|
||||
username,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 addressInfo
|
||||
* @returns
|
||||
*/
|
||||
<span class="fstat-no" title="function not covered" > async </span>export(
|
||||
serviceInterfaces: ServiceInterfaceBuilder[],
|
||||
): Promise<AddressInfo[] & AddressReceipt> {
|
||||
const addressesInfo = <span class="cstat-no" title="statement not covered" >[]</span>
|
||||
<span class="cstat-no" title="statement not covered" > for (let serviceInterface of serviceInterfaces) {</span>
|
||||
const {
|
||||
name,
|
||||
description,
|
||||
hasPrimary,
|
||||
disabled,
|
||||
id,
|
||||
type,
|
||||
username,
|
||||
path,
|
||||
search,
|
||||
schemeOverride,
|
||||
masked,
|
||||
} = <span class="cstat-no" title="statement not covered" >serviceInterface.options</span>
|
||||
|
||||
const addressInfo = <span class="cstat-no" title="statement not covered" >this.build({</span>
|
||||
username,
|
||||
path,
|
||||
search,
|
||||
schemeOverride,
|
||||
})
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > await serviceInterface.options.effects.exportServiceInterface({</span>
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
hasPrimary,
|
||||
disabled,
|
||||
addressInfo,
|
||||
type,
|
||||
masked,
|
||||
})
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > addressesInfo.push(addressInfo)</span>
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return addressesInfo as AddressInfo[] & AddressReceipt</span>
|
||||
}
|
||||
}
|
||||
|
||||
type BuildOptions = {
|
||||
schemeOverride: { ssl: Scheme; noSsl: Scheme } | null
|
||||
username: string | null
|
||||
path: string
|
||||
search: Record<string, string>
|
||||
}
|
||||
</pre></td></tr></table></pre>
|
||||
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-07-04T02:41:23.401Z
|
||||
</div>
|
||||
<script src="../prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="../sorter.js"></script>
|
||||
<script src="../block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,196 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Code coverage report for interfaces</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="../prettify.css" />
|
||||
<link rel="stylesheet" href="../base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(../sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1><a href="../index.html">All files</a> interfaces</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">16.27% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">7/43</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">0/24</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">0/11</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">17.07% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">7/41</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line low"></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-col="file" data-fmt="html" data-html="true" class="file">
|
||||
File
|
||||
</th>
|
||||
<th
|
||||
data-col="pic"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
data-html="true"
|
||||
class="pic"
|
||||
></th>
|
||||
<th
|
||||
data-col="statements"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Statements
|
||||
</th>
|
||||
<th
|
||||
data-col="statements_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="branches"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Branches
|
||||
</th>
|
||||
<th
|
||||
data-col="branches_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="functions"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Functions
|
||||
</th>
|
||||
<th
|
||||
data-col="functions_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="lines"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Lines
|
||||
</th>
|
||||
<th
|
||||
data-col="lines_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="file low" data-value="Host.ts">
|
||||
<a href="Host.ts.html">Host.ts</a>
|
||||
</td>
|
||||
<td data-value="22.22" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 22%"></div>
|
||||
<div class="cover-empty" style="width: 78%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="22.22" class="pct low">22.22%</td>
|
||||
<td data-value="27" class="abs low">6/27</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="18" class="abs low">0/18</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="7" class="abs low">0/7</td>
|
||||
<td data-value="24" class="pct low">24%</td>
|
||||
<td data-value="25" class="abs low">6/25</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="Origin.ts">
|
||||
<a href="Origin.ts.html">Origin.ts</a>
|
||||
</td>
|
||||
<td data-value="6.25" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 6%"></div>
|
||||
<div class="cover-empty" style="width: 94%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="6.25" class="pct low">6.25%</td>
|
||||
<td data-value="16" class="abs low">1/16</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="6" class="abs low">0/6</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="4" class="abs low">0/4</td>
|
||||
<td data-value="6.25" class="pct low">6.25%</td>
|
||||
<td data-value="16" class="abs low">1/16</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-07-04T02:41:23.401Z
|
||||
</div>
|
||||
<script src="../prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="../sorter.js"></script>
|
||||
<script src="../block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,141 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Code coverage report for lib/Dependency.ts</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="../prettify.css" />
|
||||
<link rel="stylesheet" href="../base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(../sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1>
|
||||
<a href="../index.html">All files</a> /
|
||||
<a href="index.html">lib</a> Dependency.ts
|
||||
</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">50% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">1/2</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">0/0</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">0/1</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">50% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">1/2</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line medium"></div>
|
||||
<pre><table class="coverage">
|
||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
||||
<a name='L2'></a><a href='#L2'>2</a>
|
||||
<a name='L3'></a><a href='#L3'>3</a>
|
||||
<a name='L4'></a><a href='#L4'>4</a>
|
||||
<a name='L5'></a><a href='#L5'>5</a>
|
||||
<a name='L6'></a><a href='#L6'>6</a>
|
||||
<a name='L7'></a><a href='#L7'>7</a>
|
||||
<a name='L8'></a><a href='#L8'>8</a>
|
||||
<a name='L9'></a><a href='#L9'>9</a>
|
||||
<a name='L10'></a><a href='#L10'>10</a>
|
||||
<a name='L11'></a><a href='#L11'>11</a>
|
||||
<a name='L12'></a><a href='#L12'>12</a>
|
||||
<a name='L13'></a><a href='#L13'>13</a>
|
||||
<a name='L14'></a><a href='#L14'>14</a>
|
||||
<a name='L15'></a><a href='#L15'>15</a>
|
||||
<a name='L16'></a><a href='#L16'>16</a>
|
||||
<a name='L17'></a><a href='#L17'>17</a>
|
||||
<a name='L18'></a><a href='#L18'>18</a>
|
||||
<a name='L19'></a><a href='#L19'>19</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import { VersionRange } from "./exver"
|
||||
|
||||
export class Dependency {
|
||||
<span class="fstat-no" title="function not covered" > constructor(</span>
|
||||
readonly <span class="cstat-no" title="statement not covered" >data:</span>
|
||||
| {
|
||||
type: "running"
|
||||
versionRange: VersionRange
|
||||
registryUrl: string
|
||||
healthChecks: string[]
|
||||
}
|
||||
| {
|
||||
type: "exists"
|
||||
versionRange: VersionRange
|
||||
registryUrl: string
|
||||
},
|
||||
) {}
|
||||
}
|
||||
</pre></td></tr></table></pre>
|
||||
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-08-28T21:46:45.516Z
|
||||
</div>
|
||||
<script src="../prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="../sorter.js"></script>
|
||||
<script src="../block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,354 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Code coverage report for lib/actions/createAction.ts</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="../../prettify.css" />
|
||||
<link rel="stylesheet" href="../../base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(../../sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1>
|
||||
<a href="../../index.html">All files</a> /
|
||||
<a href="index.html">lib/actions</a> createAction.ts
|
||||
</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">11.76% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">2/17</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">0/2</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">0/7</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">11.76% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">2/17</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line low"></div>
|
||||
<pre><table class="coverage">
|
||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
||||
<a name='L2'></a><a href='#L2'>2</a>
|
||||
<a name='L3'></a><a href='#L3'>3</a>
|
||||
<a name='L4'></a><a href='#L4'>4</a>
|
||||
<a name='L5'></a><a href='#L5'>5</a>
|
||||
<a name='L6'></a><a href='#L6'>6</a>
|
||||
<a name='L7'></a><a href='#L7'>7</a>
|
||||
<a name='L8'></a><a href='#L8'>8</a>
|
||||
<a name='L9'></a><a href='#L9'>9</a>
|
||||
<a name='L10'></a><a href='#L10'>10</a>
|
||||
<a name='L11'></a><a href='#L11'>11</a>
|
||||
<a name='L12'></a><a href='#L12'>12</a>
|
||||
<a name='L13'></a><a href='#L13'>13</a>
|
||||
<a name='L14'></a><a href='#L14'>14</a>
|
||||
<a name='L15'></a><a href='#L15'>15</a>
|
||||
<a name='L16'></a><a href='#L16'>16</a>
|
||||
<a name='L17'></a><a href='#L17'>17</a>
|
||||
<a name='L18'></a><a href='#L18'>18</a>
|
||||
<a name='L19'></a><a href='#L19'>19</a>
|
||||
<a name='L20'></a><a href='#L20'>20</a>
|
||||
<a name='L21'></a><a href='#L21'>21</a>
|
||||
<a name='L22'></a><a href='#L22'>22</a>
|
||||
<a name='L23'></a><a href='#L23'>23</a>
|
||||
<a name='L24'></a><a href='#L24'>24</a>
|
||||
<a name='L25'></a><a href='#L25'>25</a>
|
||||
<a name='L26'></a><a href='#L26'>26</a>
|
||||
<a name='L27'></a><a href='#L27'>27</a>
|
||||
<a name='L28'></a><a href='#L28'>28</a>
|
||||
<a name='L29'></a><a href='#L29'>29</a>
|
||||
<a name='L30'></a><a href='#L30'>30</a>
|
||||
<a name='L31'></a><a href='#L31'>31</a>
|
||||
<a name='L32'></a><a href='#L32'>32</a>
|
||||
<a name='L33'></a><a href='#L33'>33</a>
|
||||
<a name='L34'></a><a href='#L34'>34</a>
|
||||
<a name='L35'></a><a href='#L35'>35</a>
|
||||
<a name='L36'></a><a href='#L36'>36</a>
|
||||
<a name='L37'></a><a href='#L37'>37</a>
|
||||
<a name='L38'></a><a href='#L38'>38</a>
|
||||
<a name='L39'></a><a href='#L39'>39</a>
|
||||
<a name='L40'></a><a href='#L40'>40</a>
|
||||
<a name='L41'></a><a href='#L41'>41</a>
|
||||
<a name='L42'></a><a href='#L42'>42</a>
|
||||
<a name='L43'></a><a href='#L43'>43</a>
|
||||
<a name='L44'></a><a href='#L44'>44</a>
|
||||
<a name='L45'></a><a href='#L45'>45</a>
|
||||
<a name='L46'></a><a href='#L46'>46</a>
|
||||
<a name='L47'></a><a href='#L47'>47</a>
|
||||
<a name='L48'></a><a href='#L48'>48</a>
|
||||
<a name='L49'></a><a href='#L49'>49</a>
|
||||
<a name='L50'></a><a href='#L50'>50</a>
|
||||
<a name='L51'></a><a href='#L51'>51</a>
|
||||
<a name='L52'></a><a href='#L52'>52</a>
|
||||
<a name='L53'></a><a href='#L53'>53</a>
|
||||
<a name='L54'></a><a href='#L54'>54</a>
|
||||
<a name='L55'></a><a href='#L55'>55</a>
|
||||
<a name='L56'></a><a href='#L56'>56</a>
|
||||
<a name='L57'></a><a href='#L57'>57</a>
|
||||
<a name='L58'></a><a href='#L58'>58</a>
|
||||
<a name='L59'></a><a href='#L59'>59</a>
|
||||
<a name='L60'></a><a href='#L60'>60</a>
|
||||
<a name='L61'></a><a href='#L61'>61</a>
|
||||
<a name='L62'></a><a href='#L62'>62</a>
|
||||
<a name='L63'></a><a href='#L63'>63</a>
|
||||
<a name='L64'></a><a href='#L64'>64</a>
|
||||
<a name='L65'></a><a href='#L65'>65</a>
|
||||
<a name='L66'></a><a href='#L66'>66</a>
|
||||
<a name='L67'></a><a href='#L67'>67</a>
|
||||
<a name='L68'></a><a href='#L68'>68</a>
|
||||
<a name='L69'></a><a href='#L69'>69</a>
|
||||
<a name='L70'></a><a href='#L70'>70</a>
|
||||
<a name='L71'></a><a href='#L71'>71</a>
|
||||
<a name='L72'></a><a href='#L72'>72</a>
|
||||
<a name='L73'></a><a href='#L73'>73</a>
|
||||
<a name='L74'></a><a href='#L74'>74</a>
|
||||
<a name='L75'></a><a href='#L75'>75</a>
|
||||
<a name='L76'></a><a href='#L76'>76</a>
|
||||
<a name='L77'></a><a href='#L77'>77</a>
|
||||
<a name='L78'></a><a href='#L78'>78</a>
|
||||
<a name='L79'></a><a href='#L79'>79</a>
|
||||
<a name='L80'></a><a href='#L80'>80</a>
|
||||
<a name='L81'></a><a href='#L81'>81</a>
|
||||
<a name='L82'></a><a href='#L82'>82</a>
|
||||
<a name='L83'></a><a href='#L83'>83</a>
|
||||
<a name='L84'></a><a href='#L84'>84</a>
|
||||
<a name='L85'></a><a href='#L85'>85</a>
|
||||
<a name='L86'></a><a href='#L86'>86</a>
|
||||
<a name='L87'></a><a href='#L87'>87</a>
|
||||
<a name='L88'></a><a href='#L88'>88</a>
|
||||
<a name='L89'></a><a href='#L89'>89</a>
|
||||
<a name='L90'></a><a href='#L90'>90</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import * as T from "../types"
|
||||
import { Config, ExtractConfigType } from "../config/builder/config"
|
||||
|
||||
import { ActionMetadata, ActionResult, Effects, ExportedAction } from "../types"
|
||||
|
||||
export type MaybeFn<Manifest extends T.Manifest, Store, Value> =
|
||||
| Value
|
||||
| ((options: { effects: Effects }) => Promise<Value> | Value)
|
||||
export class CreatedAction<
|
||||
Manifest extends T.Manifest,
|
||||
Store,
|
||||
ConfigType extends
|
||||
| Record<string, any>
|
||||
| Config<any, Store>
|
||||
| Config<any, never>,
|
||||
Type extends Record<string, any> = ExtractConfigType<ConfigType>,
|
||||
> {
|
||||
<span class="fstat-no" title="function not covered" > private constructor(</span>
|
||||
public readonly <span class="cstat-no" title="statement not covered" >id: string,</span>
|
||||
public readonly <span class="cstat-no" title="statement not covered" >myMetadata: MaybeFn<</span>
|
||||
Manifest,
|
||||
Store,
|
||||
Omit<ActionMetadata, "input">
|
||||
>,
|
||||
readonly <span class="cstat-no" title="statement not covered" >fn: (options: {</span>
|
||||
effects: Effects
|
||||
input: Type
|
||||
}) => Promise<ActionResult>,
|
||||
readonly <span class="cstat-no" title="statement not covered" >input: Config<Type, Store>,</span>
|
||||
public <span class="cstat-no" title="statement not covered" >validator = <span class="branch-0 cbranch-no" title="branch not covered" >i</span>nput.validator,</span>
|
||||
) {}
|
||||
|
||||
<span class="fstat-no" title="function not covered" > static </span>of<
|
||||
Manifest extends T.Manifest,
|
||||
Store,
|
||||
ConfigType extends
|
||||
| Record<string, any>
|
||||
| Config<any, any>
|
||||
| Config<any, never>,
|
||||
Type extends Record<string, any> = ExtractConfigType<ConfigType>,
|
||||
>(
|
||||
id: string,
|
||||
metadata: MaybeFn<Manifest, Store, Omit<ActionMetadata, "input">>,
|
||||
fn: (options: { effects: Effects; input: Type }) => Promise<ActionResult>,
|
||||
inputConfig: Config<Type, Store> | Config<Type, never>,
|
||||
) {
|
||||
<span class="cstat-no" title="statement not covered" > return new CreatedAction<Manifest, Store, ConfigType, Type>(</span>
|
||||
id,
|
||||
metadata,
|
||||
fn,
|
||||
inputConfig as Config<Type, Store>,
|
||||
)
|
||||
}
|
||||
|
||||
exportedAction: ExportedAction = <span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >({</span> effects, input }) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > return this.fn({</span>
|
||||
effects,
|
||||
input: this.validator.unsafeCast(input),
|
||||
})
|
||||
}
|
||||
|
||||
run = <span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >async </span>({ effects, input }: { effects: Effects; input?: Type }) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > return this.fn({</span>
|
||||
effects,
|
||||
input: this.validator.unsafeCast(input),
|
||||
})
|
||||
}
|
||||
|
||||
<span class="fstat-no" title="function not covered" > async </span>metadata(options: { effects: Effects }) {
|
||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (this.myMetadata instanceof Function)</span>
|
||||
<span class="cstat-no" title="statement not covered" > return await this.myMetadata(options)</span>
|
||||
<span class="cstat-no" title="statement not covered" > return this.myMetadata</span>
|
||||
}
|
||||
|
||||
<span class="fstat-no" title="function not covered" > async </span>ActionMetadata(options: { effects: Effects }): Promise<ActionMetadata> {
|
||||
<span class="cstat-no" title="statement not covered" > return {</span>
|
||||
...(await this.metadata(options)),
|
||||
input: await this.input.build(options),
|
||||
}
|
||||
}
|
||||
|
||||
<span class="fstat-no" title="function not covered" > async </span>getConfig({ effects }: { effects: Effects }) {
|
||||
<span class="cstat-no" title="statement not covered" > return this.input.build({</span>
|
||||
effects,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const createAction = CreatedAction.of
|
||||
</pre></td></tr></table></pre>
|
||||
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-08-28T21:46:45.516Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="../../sorter.js"></script>
|
||||
<script src="../../block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,196 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Code coverage report for lib/actions</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="../../prettify.css" />
|
||||
<link rel="stylesheet" href="../../base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type="text/css">
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(../../sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="pad1">
|
||||
<h1><a href="../../index.html">All files</a> lib/actions</h1>
|
||||
<div class="clearfix">
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">10.71% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class="fraction">3/28</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class="fraction">0/2</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class="fraction">0/12</span>
|
||||
</div>
|
||||
|
||||
<div class="fl pad1y space-right2">
|
||||
<span class="strong">10.71% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class="fraction">3/28</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block,
|
||||
<em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input oninput="onInput()" type="search" id="fileSearch" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="status-line low"></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-col="file" data-fmt="html" data-html="true" class="file">
|
||||
File
|
||||
</th>
|
||||
<th
|
||||
data-col="pic"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
data-html="true"
|
||||
class="pic"
|
||||
></th>
|
||||
<th
|
||||
data-col="statements"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Statements
|
||||
</th>
|
||||
<th
|
||||
data-col="statements_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="branches"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Branches
|
||||
</th>
|
||||
<th
|
||||
data-col="branches_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="functions"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Functions
|
||||
</th>
|
||||
<th
|
||||
data-col="functions_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
<th
|
||||
data-col="lines"
|
||||
data-type="number"
|
||||
data-fmt="pct"
|
||||
class="pct"
|
||||
>
|
||||
Lines
|
||||
</th>
|
||||
<th
|
||||
data-col="lines_raw"
|
||||
data-type="number"
|
||||
data-fmt="html"
|
||||
class="abs"
|
||||
></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="file low" data-value="createAction.ts">
|
||||
<a href="createAction.ts.html">createAction.ts</a>
|
||||
</td>
|
||||
<td data-value="11.76" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 11%"></div>
|
||||
<div class="cover-empty" style="width: 89%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="11.76" class="pct low">11.76%</td>
|
||||
<td data-value="17" class="abs low">2/17</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="2" class="abs low">0/2</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="7" class="abs low">0/7</td>
|
||||
<td data-value="11.76" class="pct low">11.76%</td>
|
||||
<td data-value="17" class="abs low">2/17</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="setupActions.ts">
|
||||
<a href="setupActions.ts.html">setupActions.ts</a>
|
||||
</td>
|
||||
<td data-value="9.09" class="pic low">
|
||||
<div class="chart">
|
||||
<div class="cover-fill" style="width: 9%"></div>
|
||||
<div class="cover-empty" style="width: 91%"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="9.09" class="pct low">9.09%</td>
|
||||
<td data-value="11" class="abs low">1/11</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="0" class="abs high">0/0</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="5" class="abs low">0/5</td>
|
||||
<td data-value="9.09" class="pct low">9.09%</td>
|
||||
<td data-value="11" class="abs low">1/11</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="push"></div>
|
||||
<!-- for sticky footer -->
|
||||
</div>
|
||||
<!-- /wrapper -->
|
||||
<div class="footer quiet pad2 space-top1 center small">
|
||||
Code coverage generated by
|
||||
<a
|
||||
href="https://istanbul.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>istanbul</a
|
||||
>
|
||||
at 2024-08-28T21:46:45.516Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="../../sorter.js"></script>
|
||||
<script src="../../block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user