export * as configTypes from "./config/configTypes" import { InputSpec } from "./config/configTypes" import { DependenciesReceipt } from "./config/setupConfig" import { PortOptions } from "./interfaces/Host" import { UrlString } from "./util/getNetworkInterface" import { NetworkInterfaceType } from "./util/utils" export type Signals = "SIGINT" | "SIGTERM" | "SIGKILL" | "SIGHUP" export type ExportedAction = (options: { effects: Effects input?: Record }) => Promise export type MaybePromise = A | Promise export namespace ExpectedExports { version: 1 /** Set configuration is called after we have modified and saved the configuration in the start9 ui. Use this to make a file for the docker to read from for configuration. */ export type setConfig = (options: { effects: Effects input: Record }) => Promise /** Get configuration returns a shape that describes the format that the start9 ui will generate, and later send to the set config */ export type getConfig = (options: { effects: Effects }) => Promise // /** These are how we make sure the our dependency configurations are valid and if not how to fix them. */ // export type dependencies = Dependencies; /** For backing up service data though the startOS UI */ export type createBackup = (options: { effects: Effects }) => Promise /** 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 // /** Health checks are used to determine if the service is working properly after starting // * A good use case is if we are using a web server, seeing if we can get to the web server. // */ // export type health = { // /** Should be the health check id */ // [id: string]: (options: { effects: Effects; input: TimeMs }) => Promise; // }; /** * Actions are used so we can effect the service, like deleting a directory. * One old use case is to add a action where we add a file, that will then be run during the * service starting, and that file would indicate that it would rescan all the data. */ export type actions = (options: { effects: Effects }) => MaybePromise<{ [id: string]: { run: ExportedAction getConfig: (options: { effects: Effects }) => Promise } }> export type actionsMetadata = (options: { effects: Effects }) => Promise> /** * This is the entrypoint for the main container. Used to start up something like the service that the * package represents, like running a bitcoind in a bitcoind-wrapper. */ export type main = (options: { effects: Effects started(onTerm: () => void): null }) => Promise /** * 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 /** * Every time a package completes an install, this function is called before the main. * Can be used to do migration like things. */ export type init = (options: { effects: Effects previousVersion: null | string }) => Promise /** This will be ran during any time a package is uninstalled, for example during a update * this will be called. */ export type uninit = (options: { effects: Effects nextVersion: null | string }) => Promise /** Auto configure is used to make sure that other dependencies have the values t * that this service could use. */ export type dependencyConfig = Record } export type TimeMs = number export type VersionString = string /** * AutoConfigure is used as the value to the key of package id, * this is used to make sure that other dependencies have the values that this service could use. */ export type DependencyConfig = { /** During autoconfigure, we have access to effects and local data. We are going to figure out all the data that we need and send it to update. For the sdk it is the desired delta */ query(options: { effects: Effects; localConfig: unknown }): Promise /** This is the second part. Given the query results off the previous function, we will determine what to change the remote config to. In our sdk normall we are going to use the previous as a deep merge. */ update(options: { queryResults: unknown remoteConfig: unknown }): Promise } export type ValidIfNoStupidEscape = A extends | `${string}'"'"'${string}` | `${string}\\"${string}` ? never : "" extends A & "" ? never : A export type ConfigRes = { /** This should be the previous config, that way during set config we start with the previous */ config?: null | Record /** Shape that is describing the form in the ui */ spec: InputSpec } declare const DaemonProof: unique symbol export type DaemonReceipt = { [DaemonProof]: never } export type Daemon = { wait(): Promise term(options?: { signal?: Signals; timeout?: number }): Promise [DaemonProof]: never } export type HealthStatus = "passing" | "warning" | "failing" | "disabled" export type SmtpValue = { server: string port: number from: string login: string password: string | null | undefined } export type CommandType = | ValidIfNoStupidEscape | [string, ...string[]] export type DaemonReturned = { wait(): Promise term(options?: { signal?: Signals; timeout?: number }): Promise } export type ActionMetadata = { name: string description: string id: string input: InputSpec allowedStatuses: "only-running" | "only-stopped" | "any" | "disabled" /** * So the ordering of the actions is by alphabetical order of the group, then followed by the alphabetical of the actions */ group?: string } export declare const hostName: unique symbol export type HostName = string & { [hostName]: never } /** ${scheme}://${username}@${host}:${externalPort}${suffix} */ export type Address = { username: string | null hostId: string options: PortOptions suffix: string } export type InterfaceId = string export type NetworkInterface = { interfaceId: InterfaceId /** The title of this field to be displayed */ name: string /** Human readable description, used as tooltip usually */ description: string /** Whether or not one address must be the primary address */ hasPrimary: boolean /** Disabled interfaces do not serve, but they retain their metadata and addresses */ disabled: boolean /** All URIs */ addresses: Address[] /** The netowrk interface could be serveral types, something like ui, p2p, or network */ type: NetworkInterfaceType } // prettier-ignore export type ExposeAllServicePaths = Store extends Record ? {[K in keyof Store & string]: ExposeAllServicePaths}[keyof Store & string] : PreviousPath // prettier-ignore export type ExposeAllUiPaths = Store extends Record ? {[K in keyof Store & string]: ExposeAllUiPaths}[keyof Store & string] : Store extends string ? PreviousPath : never export type ExposeServicePaths = Array<{ /** The path to the value in the Store. [JsonPath](https://jsonpath.com/) */ path: ExposeAllServicePaths }> export type ExposeUiPaths = Array<{ /** The path to the value in the Store. [JsonPath](https://jsonpath.com/) */ path: ExposeAllUiPaths /** A human readable title for the value */ title: string /** A human readable description or explanation of the value */ description?: string /** (string/number only) Whether or not to mask the value, for example, when displaying a password */ masked?: boolean /** (string/number only) Whether or not to include a button for copying the value to clipboard */ copyable?: boolean /** (string/number only) Whether or not to include a button for displaying the value as a QR code */ qr?: boolean }> /** Used to reach out from the pure js runtime */ export type Effects = { executeAction(opts: { serviceId?: string actionId: string input: Input }): Promise /** Removes all network bindings */ clearBindings(): Promise /** Creates a host connected to the specified port with the provided options */ bind( options: { kind: "static" | "single" | "multi" id: string internalPort: number } & PortOptions, ): Promise /** Retrieves the current hostname(s) associated with a host id */ getHostnames(options: { kind: "static" | "single" hostId: string packageId?: string callback: () => void }): Promise<[HostName]> getHostnames(options: { kind?: "multi" packageId?: string hostId: string callback: () => void }): Promise<[HostName, ...HostName[]]> // /** // * Run rsync between two volumes. This is used to backup data between volumes. // * This is a long running process, and a structure that we can either wait for, or get the progress of. // */ // runRsync(options: { // srcVolume: string // dstVolume: string // srcPath: string // dstPath: string // // rsync options: https://linux.die.net/man/1/rsync // options: BackupOptions // }): { // id: () => Promise // wait: () => Promise // progress: () => Promise // } store: { /** Get a value in a json like data, can be observed and subscribed */ get(options: { /** If there is no packageId it is assumed the current package */ packageId?: string /** The path defaults to root level, using the [JsonPath](https://jsonpath.com/) */ path: Path & EnsureStorePath callback: (config: unknown, previousConfig: unknown) => void }): Promise> /** Used to store values that can be accessed and subscribed to */ set(options: { /** Sets the value for the wrapper at the path, it will override, using the [JsonPath](https://jsonpath.com/) */ path: Path & EnsureStorePath value: ExtractStore }): Promise } getSystemSmtp(input: { callback: (config: unknown, previousConfig: unknown) => void }): Promise getLocalHostname(): Promise getIPHostname(): Promise /** Get the address for another service for tor interfaces */ getServiceTorHostname( interfaceId: InterfaceId, packageId?: string, ): Promise /** Get the IP address of the container */ getContainerIp(): Promise /** * Get the port address for another service */ getServicePortForward( internalPort: number, packageId?: string, ): Promise /** Removes all network interfaces */ clearNetworkInterfaces(): Promise /** When we want to create a link in the front end interfaces, and example is * exposing a url to view a web service */ exportNetworkInterface(options: NetworkInterface): Promise exposeForDependents( options: ExposeServicePaths, ): Promise exposeUi(options: ExposeUiPaths): Promise /** * There are times that we want to see the addresses that where exported * @param options.addressId If we want to filter the address id * * Note: any auth should be filtered out already */ getInterface(options: { packageId?: PackageId interfaceId: InterfaceId callback: () => void }): Promise /** * The user sets the primary url for a interface * @param options */ getPrimaryUrl(options: { packageId?: PackageId interfaceId: InterfaceId callback: () => void }): Promise /** * There are times that we want to see the addresses that where exported * @param options.addressId If we want to filter the address id * * Note: any auth should be filtered out already */ listInterface(options: { packageId?: PackageId callback: () => void }): Promise /** *Remove an address that was exported. Used problably during main or during setConfig. * @param options */ removeAddress(options: { id: string }): Promise /** * * @param options */ exportAction(options: ActionMetadata): Promise /** * Remove an action that was exported. Used problably during main or during setConfig. */ removeAction(options: { id: string }): Promise getConfigured(): Promise /** * This called after a valid set config as well as during init. * @param configured */ setConfigured(configured: boolean): Promise /** * * @returns PEM encoded fullchain (ecdsa) */ getSslCertificate: ( packageId: string, algorithm?: "ecdsa" | "ed25519", ) => Promise<[string, string, string]> /** * @returns PEM encoded ssl key (ecdsa) */ getSslKey: ( packageId: string, algorithm?: "ecdsa" | "ed25519", ) => Promise setHealth(o: { name: string status: HealthStatus message?: string }): Promise setMainStatus(o: { status: "running" | "stopped" }): Promise /** Set the dependencies of what the service needs, usually ran during the set config as a best practice */ setDependencies(dependencies: Dependencies): Promise /** Exists could be useful during the runtime to know if some service exists, option dep */ exists(packageId: PackageId): Promise /** Exists could be useful during the runtime to know if some service is running, option dep */ running(packageId: PackageId): Promise /** Instead of creating proxies with nginx, we have a utility to create and maintain a proxy in the lifetime of this running. */ reverseProxy(options: { bind: { /** Optional, default is 0.0.0.0 */ ip?: string port: number ssl: boolean } dst: { /** Optional: default is 127.0.0.1 */ ip?: string // optional, default 127.0.0.1 port: number ssl: boolean } http?: { // optional, will do TCP layer proxy only if not present headers?: (headers: Record) => Record } }): Promise<{ stop(): Promise }> restart(): void shutdown(): void mount(options: { location: { /** If there is no volumeId then we mount to runMedia a special mounting location */ volumeId?: string path: string } target: { packageId: string volumeId: string path: string readonly: boolean } }): Promise stopped(packageId?: string): Promise } // prettier-ignore export type ExtractStore = Path extends `/${infer A }/${infer Rest }` ? (A extends keyof Store ? ExtractStore : never) : Path extends `/${infer A }` ? (A extends keyof Store ? Store[A] : never) : Path extends '' ? Store : never // prettier-ignore type _EnsureStorePath = Path extends`/${infer A }/${infer Rest}` ? (Store extends {[K in A & string]: infer NextStore} ? _EnsureStorePath : never) : Path extends `/${infer A }` ? (Store extends {[K in A]: infer B} ? Origin : never) : Path extends '' ? Origin : never // prettier-ignore export type EnsureStorePath = _EnsureStorePath /** rsync options: https://linux.die.net/man/1/rsync */ export type BackupOptions = { delete: boolean force: boolean ignoreExisting: boolean exclude: string[] } /** * This is the metadata that is returned from the metadata call. */ export type Metadata = { fileType: string isDir: boolean isFile: boolean isSymlink: boolean len: number modified?: Date accessed?: Date created?: Date readonly: boolean uid: number gid: number mode: number } export type MigrationRes = { configured: boolean } export type ActionResult = { message: string value: null | { value: string copyable: boolean qr: boolean } } export type SetResult = { /** These are the unix process signals */ signal: | "SIGTERM" | "SIGHUP" | "SIGINT" | "SIGQUIT" | "SIGILL" | "SIGTRAP" | "SIGABRT" | "SIGBUS" | "SIGFPE" | "SIGKILL" | "SIGUSR1" | "SIGSEGV" | "SIGUSR2" | "SIGPIPE" | "SIGALRM" | "SIGSTKFLT" | "SIGCHLD" | "SIGCONT" | "SIGSTOP" | "SIGTSTP" | "SIGTTIN" | "SIGTTOU" | "SIGURG" | "SIGXCPU" | "SIGXFSZ" | "SIGVTALRM" | "SIGPROF" | "SIGWINCH" | "SIGIO" | "SIGPWR" | "SIGSYS" | "SIGEMT" | "SIGINFO" "depends-on": DependsOn } export type PackageId = string export type Message = string export type DependencyKind = "running" | "exists" export type DependsOn = { [packageId: string]: string[] } export type KnownError = | { error: string } | { "error-code": [number, string] | readonly [number, string] } export type Dependency = { id: PackageId kind: DependencyKind } export type Dependencies = Array export type DeepPartial = T extends {} ? { [P in keyof T]?: DeepPartial } : T