diff --git a/lib/config/builder/config.ts b/lib/config/builder/config.ts
index 7ba7ccb..8824759 100644
--- a/lib/config/builder/config.ts
+++ b/lib/config/builder/config.ts
@@ -501,10 +501,8 @@ export class Config extends IBuilder {
}
static of }>(spec: B) {
- // deno-lint-ignore no-explicit-any
const answer: { [K in keyof B]: BuilderExtract } = {} as any;
for (const key in spec) {
- // deno-lint-ignore no-explicit-any
answer[key] = spec[key].build() as any;
}
return new Config(answer);
diff --git a/lib/config/builder/variants.ts b/lib/config/builder/variants.ts
index 7001557..0b61b0a 100644
--- a/lib/config/builder/variants.ts
+++ b/lib/config/builder/variants.ts
@@ -39,17 +39,15 @@ import { Config } from ".";
```
*/
export class Variants<
- A extends { [key: string]: ConfigSpec }
+ A extends { [key: string]: ConfigSpec },
> extends IBuilder {
static of<
A extends {
[key: string]: Config;
- }
+ },
>(a: A) {
- // deno-lint-ignore no-explicit-any
const variants: { [K in keyof A]: BuilderExtract } = {} as any;
for (const key in a) {
- // deno-lint-ignore no-explicit-any
variants[key] = a[key].build() as any;
}
return new Variants(variants);
@@ -60,14 +58,14 @@ export class Variants<
}
static withVariant(
key: K,
- value: Config
+ value: Config,
) {
return Variants.empty().withVariant(key, value);
}
withVariant(
key: K,
- value: Config
+ value: Config,
) {
return new Variants({
...this.a,
diff --git a/lib/config/setup_config_export.ts b/lib/config/setup_config_export.ts
index d4d8da2..5a3d702 100644
--- a/lib/config/setup_config_export.ts
+++ b/lib/config/setup_config_export.ts
@@ -4,6 +4,13 @@ import { ConfigSpec } from "../types/config-types";
import { nullIfEmpty, okOf } from "../util";
import { TypeFromProps } from "../util/propertiesMatcher";
+/**
+ * We want to setup a config export with a get and set, this
+ * is going to be the default helper to setup config, because it will help
+ * enforce that we have a spec, write, and reading.
+ * @param options
+ * @returns
+ */
export function setupConfigExports(options: {
spec: Config;
dependsOn: DependsOn;
diff --git a/lib/health/healthRunner.ts b/lib/health/healthRunner.ts
index 4311cfc..13b594c 100644
--- a/lib/health/healthRunner.ts
+++ b/lib/health/healthRunner.ts
@@ -3,7 +3,7 @@ import { object, string } from "ts-matches";
export type HealthCheck = (
effects: Types.Effects,
- dateMs: number
+ dateMs: number,
) => Promise;
export type HealthResult =
| { success: string }
@@ -23,16 +23,23 @@ function safelyStringify(e: unknown) {
}
async function timeoutHealth(
effects: Types.Effects,
- timeMs: number
+ timeMs: number,
): Promise {
await effects.sleep(timeMs);
return { failure: "Timed out " };
}
+/**
+ * Health runner is usually used during the main function, and will be running in a loop.
+ * This health check then will be run every intervalS seconds.
+ * The return needs a create()
+ * then from there we need a start().
+ * The stop function is used to stop the health check.
+ */
export default function healthRunner(
name: string,
fn: HealthCheck,
- { defaultIntervalS = 60 } = {}
+ { defaultIntervalS = 60 } = {},
) {
return {
create(effects: Types.Effects, defaultIntervalCreatedS = defaultIntervalS) {
diff --git a/lib/health/util.ts b/lib/health/util.ts
index 0fed391..fdc227a 100644
--- a/lib/health/util.ts
+++ b/lib/health/util.ts
@@ -1,15 +1,23 @@
import { Effects, ResultType } from "../types";
-import { error, errorCode, isKnownError, ok } from "../util";
+import { error, errorCode, isKnownError, ok, okOf } from "../util";
+import { HealthResult } from "./healthRunner";
+
+/**
+ * This is a helper function to check if a web url is reachable.
+ * @param url
+ * @param createSuccess
+ * @returns
+ */
export const checkWebUrl: (
- url: string
+ url: string,
+ createSuccess?: null | ((response?: string | null) => string),
) => (
effects: Effects,
- duration: number
-) => Promise> = (url) => {
+ duration: number,
+) => Promise> = (url, createSuccess = null) => {
return async (effects, duration) => {
let errorValue;
if (
- // deno-lint-ignore no-cond-assign
(errorValue = guardDurationAboveMinimum({ duration, minimumTime: 5000 }))
) {
return errorValue;
@@ -17,7 +25,12 @@ export const checkWebUrl: (
return await effects
.fetch(url)
- .then((_) => ok)
+ .then((x) =>
+ okOf({
+ success:
+ createSuccess?.(x.body) ?? `Successfully fetched URL: ${url}`,
+ }),
+ )
.catch((e) => {
effects.warn(`Error while fetching URL: ${url}`);
effects.error(JSON.stringify(e));
@@ -27,17 +40,38 @@ export const checkWebUrl: (
};
};
+/**
+ * Running a health script, is used when we want to have a simple
+ * script in bash or something like that. It should return something that is useful
+ * in {result: string} else it is considered an error
+ * @param param0
+ * @returns
+ */
export const runHealthScript =
- ({ command, args }: { command: string; args: string[] }) =>
+ ({
+ command,
+ args,
+ message,
+ }: {
+ command: string;
+ args: string[];
+ message: ((result: unknown) => string) | null;
+ }) =>
async (
effects: Effects,
- _duration: number
- ): Promise> => {
+ _duration: number,
+ ): Promise> => {
const res = await effects.runCommand({ command, args });
if ("result" in res) {
- return { result: null };
+ return {
+ result: {
+ success:
+ message?.(res) ??
+ `Have ran script ${command} and the result: ${res.result}`,
+ },
+ };
} else {
- return res;
+ throw res;
}
};
diff --git a/lib/types.ts b/lib/types.ts
index b47a589..0d0f7eb 100644
--- a/lib/types.ts
+++ b/lib/types.ts
@@ -1,9 +1,7 @@
export * as configTypes from "./types/config-types";
import { ConfigSpec } from "./types/config-types";
-// deno-lint-ignore no-namespace
export namespace ExpectedExports {
- // deno-lint-ignore no-unused-labels
version: 1;
/** Set configuration is called after we have modified and saved the configuration in the embassy ui. Use this to make a file for the docker to read from for configuration. */
export type setConfig = (options: {
@@ -40,16 +38,6 @@ export namespace ExpectedExports {
}) => Promise>;
};
- /**
- * Migrations are used when we are changing versions when updating/ downgrading.
- * There are times that we need to move files around, and do other operations during a migration.
- */
- export type migration = (options: {
- effects: Effects;
- input: VersionString;
- args: unknown[];
- }) => 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
@@ -70,73 +58,26 @@ export namespace ExpectedExports {
effects: Effects;
started(): null;
}) => 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>;
}
export type TimeMs = number;
export type VersionString = string;
-/** @deprecated */
-// deno-lint-ignore no-namespace
-export namespace LegacyExpectedExports {
- /** Set configuration is called after we have modified and saved the configuration in the embassy ui. Use this to make a file for the docker to read from for configuration. */
- export type setConfig = (
- effects: Effects,
- input: Record
- ) => Promise>;
- /** Get configuration returns a shape that describes the format that the embassy ui will generate, and later send to the set config */
- export type getConfig = (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 embassyOS UI */
- export type createBackup = (effects: Effects) => Promise>;
- /** For restoring service data that was previously backed up using the embassyOS UI create backup flow. Backup restores are also triggered via the embassyOS UI, or doing a system restore flow during setup. */
- export type restoreBackup = (
- effects: Effects
- ) => Promise>;
- /** Properties are used to get values from the docker, like a username + password, what ports we are hosting from */
- export type properties = (
- 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]: (
- effects: Effects,
- dateMs: number
- ) => Promise>;
- };
-
- /**
- * Migrations are used when we are changing versions when updating/ downgrading.
- * There are times that we need to move files around, and do other operations during a migration.
- */
- export type migration = (
- effects: Effects,
- version: string,
- ...args: unknown[]
- ) => 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 action = {
- [id: string]: (
- effects: Effects,
- config?: ConfigSpec
- ) => 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 = (effects: Effects) => Promise>;
-}
-
export type ConfigRes = {
/** This should be the previous config, that way during set config we start with the previous */
config?: null | Record;
@@ -184,7 +125,9 @@ export type Effects = {
term(): Promise;
};
+ /** Uses the chown on the system */
chown(input: { volumeId: string; path: string; uid: string }): Promise;
+ /** Uses the chmod on the system */
chmod(input: { volumeId: string; path: string; mode: string }): Promise;
sleep(timeMs: TimeMs): Promise;
@@ -203,25 +146,31 @@ export type Effects = {
/** Sandbox mode lets us read but not write */
is_sandboxed(): boolean;
+ /** Check that a file exists or not */
exists(input: { volumeId: string; path: string }): Promise;
+ /** Declaring that we are opening a interface on some protocal for local network */
bindLocal(options: {
internalPort: number;
name: string;
externalPort: number;
}): Promise;
+ /** Declaring that we are opening a interface on some protocal for tor network */
bindTor(options: {
internalPort: number;
name: string;
externalPort: number;
}): Promise;
+ /** Similar to the fetch api via the mdn, this is simplified but the point is
+ * to get something from some website, and return the response.
+ */
fetch(
url: string,
options?: {
method?: "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "PATCH";
headers?: Record;
body?: string;
- }
+ },
): Promise<{
method: string;
ok: boolean;
@@ -234,6 +183,10 @@ export type Effects = {
json(): Promise;
}>;
+ /**
+ * 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;
@@ -246,15 +199,82 @@ export type Effects = {
wait: () => Promise;
progress: () => Promise;
};
+
+ /** Get the address for another service for local internet*/
+ getServiceLocalAddress(options: {
+ packageId: string;
+ interfaceName: string;
+ }): Promise;
+ /** Get the address for another service for tor interfaces */
+ getServiceTorAddress(options: {
+ packageId: string;
+ interfaceName: string;
+ }): Promise;
+ /**
+ * Get the port address for another service
+ */
+ getServicePortForward(options: {
+ packageId: string;
+ internalPort: number;
+ }): Promise;
+
+ /** When we want to create a link in the front end interfaces, and example is
+ * exposing a url to view a web service
+ */
+ exportAddress(options: {
+ /** The title of this field to be dsimplayed */
+ name: string;
+ /** Human readable description, used as tooltip usually */
+ description: string;
+ /** URI location */
+ address: string;
+ id: string;
+ /** Defaults to false, but describes if this address can be opened in a browser as an
+ * ui interface
+ */
+ ui?: boolean;
+ }): 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: {
+ name: string;
+ description: string;
+ id: string;
+ input: null | ConfigSpec;
+ }): 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;
};
-// rsync options: https://linux.die.net/man/1/rsync
+/* 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;
@@ -362,12 +382,12 @@ export type Dependencies = {
/** Checks are called to make sure that our dependency is in the correct shape. If a known error is returned we know that the dependency needs modification */
check(
effects: Effects,
- input: ConfigSpec
+ input: ConfigSpec,
): Promise>;
/** This is called after we know that the dependency package needs a new configuration, this would be a transform for defaults */
autoConfigure(
effects: Effects,
- input: ConfigSpec
+ input: ConfigSpec,
): Promise>;
};
};
diff --git a/lib/types/config-types.ts b/lib/types/config-types.ts
index 75155dd..9a3935b 100644
--- a/lib/types/config-types.ts
+++ b/lib/types/config-types.ts
@@ -1,4 +1,3 @@
-// deno-lint-ignore-file ban-types
export type ConfigSpec = Record;
export type ValueType =
diff --git a/lib/util/propertiesMatcher.ts b/lib/util/propertiesMatcher.ts
index ca80158..e8c2f15 100644
--- a/lib/util/propertiesMatcher.ts
+++ b/lib/util/propertiesMatcher.ts
@@ -11,7 +11,6 @@ type TypePointer = "pointer";
type TypeUnion = "union";
// prettier-ignore
-// deno-fmt-ignore
type GuardDefaultNullable =
A extends { readonly default: unknown} ? Type :
A extends { readonly nullable: true} ? Type :
@@ -19,24 +18,20 @@ type GuardDefaultNullable =
Type
// prettier-ignore
-// deno-fmt-ignore
type GuardNumber =
A extends {readonly type:TypeNumber} ? GuardDefaultNullable :
unknown
// prettier-ignore
-// deno-fmt-ignore
type GuardString =
A extends {readonly type:TypeString} ? GuardDefaultNullable :
unknown
// prettier-ignore
-// deno-fmt-ignore
type GuardBoolean =
A extends {readonly type:TypeBoolean} ? GuardDefaultNullable :
unknown
// prettier-ignore
-// deno-fmt-ignore
type GuardObject =
A extends {readonly type: TypeObject, readonly spec: infer B} ? (
B extends Record ? {readonly [K in keyof B & string]: _>} :
@@ -45,24 +40,19 @@ type GuardObject =
unknown
// prettier-ignore
-// deno-fmt-ignore
export type GuardList =
A extends {readonly type:TypeList, readonly subtype: infer B, spec?: {spec?: infer C }} ? ReadonlyArray & ({type: B, spec: C})>> :
- // deno-lint-ignore ban-types
A extends {readonly type:TypeList, readonly subtype: infer B, spec?: {}} ? ReadonlyArray & ({type: B})>> :
unknown
// prettier-ignore
-// deno-fmt-ignore
type GuardPointer =
A extends {readonly type:TypePointer} ? (string | null) :
unknown
// prettier-ignore
-// deno-fmt-ignore
type GuardEnum =
A extends {readonly type:TypeEnum, readonly values: ArrayLike} ? GuardDefaultNullable :
unknown
// prettier-ignore
-// deno-fmt-ignore
type GuardUnion =
A extends {readonly type:TypeUnion, readonly tag: {id: infer Id & string}, variants: infer Variants & Record} ? {[K in keyof Variants]: {[keyType in Id & string]: K}&TypeFromProps}[keyof Variants] :
unknown
@@ -77,7 +67,6 @@ export type GuardAll = GuardNumber &
GuardUnion &
GuardEnum;
// prettier-ignore
-// deno-fmt-ignore
export type TypeFromProps =
A extends Record ? {readonly [K in keyof A & string]: _>} :
unknown;
@@ -118,7 +107,7 @@ function charRange(value = "") {
*/
export function generateDefault(
generate: { charset: string; len: number },
- { random = () => Math.random() } = {}
+ { random = () => Math.random() } = {},
) {
const validCharSets: number[][] = generate.charset
.split(",")
@@ -129,7 +118,7 @@ export function generateDefault(
}
const max = validCharSets.reduce(
(acc, x) => x.reduce((x, y) => Math.max(x, y), acc),
- 0
+ 0,
);
let i = 0;
const answer: string[] = Array(generate.len);
@@ -138,7 +127,7 @@ export function generateDefault(
const inRange = validCharSets.reduce(
(acc, [lower, upper]) =>
acc || (nextValue >= lower && nextValue <= upper),
- false
+ false,
);
if (!inRange) continue;
answer[i] = String.fromCharCode(nextValue);
@@ -166,19 +155,17 @@ export function matchNumberWithRange(range: string) {
? "any"
: left === "["
? `greaterThanOrEqualTo${leftValue}`
- : `greaterThan${leftValue}`
+ : `greaterThan${leftValue}`,
)
.validate(
- rightValue === "*"
- ? (_) => true
- : right === "]"
- ? (x) => x <= Number(rightValue)
- : (x) => x < Number(rightValue),
- rightValue === "*"
- ? "any"
- : right === "]"
- ? `lessThanOrEqualTo${rightValue}`
- : `lessThan${rightValue}`
+ // prettier-ignore
+ rightValue === "*" ? (_) => true :
+ right === "]"? (x) => x <= Number(rightValue) :
+ (x) => x < Number(rightValue),
+ // prettier-ignore
+ rightValue === "*" ? "any" :
+ right === "]" ? `lessThanOrEqualTo${rightValue}` :
+ `lessThan${rightValue}`,
);
}
function withIntegral(parser: matches.Parser, value: unknown) {
@@ -199,12 +186,12 @@ const isGenerator = matches.shape({
}).test;
function defaultNullable(
parser: matches.Parser,
- value: unknown
+ value: unknown,
) {
if (matchDefault.test(value)) {
if (isGenerator(value.default)) {
return parser.defaultTo(
- parser.unsafeCast(generateDefault(value.default))
+ parser.unsafeCast(generateDefault(value.default)),
);
}
return parser.defaultTo(value.default);
@@ -222,34 +209,28 @@ function defaultNullable(
* @returns
*/
export function guardAll(
- value: A
+ value: A,
): matches.Parser> {
if (!isType.test(value)) {
- // deno-lint-ignore no-explicit-any
return matches.unknown as any;
}
switch (value.type) {
case "boolean":
- // deno-lint-ignore no-explicit-any
return defaultNullable(matches.boolean, value) as any;
case "string":
- // deno-lint-ignore no-explicit-any
return defaultNullable(withPattern(value), value) as any;
case "number":
return defaultNullable(
withIntegral(withRange(value), value),
- value
- // deno-lint-ignore no-explicit-any
+ value,
) as any;
case "object":
if (matchSpec.test(value)) {
- // deno-lint-ignore no-explicit-any
return defaultNullable(typeFromProps(value.spec), value) as any;
}
- // deno-lint-ignore no-explicit-any
return matches.unknown as any;
case "list": {
@@ -261,22 +242,18 @@ export function guardAll(
const subtype = matchSubType.unsafeCast(value).subtype;
return defaultNullable(
matches
- // deno-lint-ignore no-explicit-any
.arrayOf(guardAll({ type: subtype, ...spec } as any))
.validate((x) => rangeValidate(x.length), "valid length"),
- value
- // deno-lint-ignore no-explicit-any
+ value,
) as any;
}
case "enum":
if (matchValues.test(value)) {
return defaultNullable(
matches.literals(value.values[0], ...value.values),
- value
- // deno-lint-ignore no-explicit-any
+ value,
) as any;
}
- // deno-lint-ignore no-explicit-any
return matches.unknown as any;
case "union":
if (matchUnion.test(value)) {
@@ -284,15 +261,13 @@ export function guardAll(
...Object.entries(value.variants).map(([variant, spec]) =>
matches
.shape({ [value.tag.id]: matches.literal(variant) })
- .concat(typeFromProps(spec))
- ) // deno-lint-ignore no-explicit-any
+ .concat(typeFromProps(spec)),
+ ),
) as any;
}
- // deno-lint-ignore no-explicit-any
return matches.unknown as any;
}
- // deno-lint-ignore no-explicit-any
return matches.unknown as any;
}
/**
@@ -304,17 +279,15 @@ export function guardAll(
* @returns
*/
export function typeFromProps(
- valueDictionary: A
+ valueDictionary: A,
): matches.Parser> {
- // deno-lint-ignore no-explicit-any
if (!recordString.test(valueDictionary)) return matches.unknown as any;
return matches.shape(
Object.fromEntries(
Object.entries(valueDictionary).map(([key, value]) => [
key,
guardAll(value),
- ])
- )
- // deno-lint-ignore no-explicit-any
+ ]),
+ ),
) as any;
}
diff --git a/package-lock.json b/package-lock.json
index dcda1ff..08fa0ad 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "start-sdk",
- "version": "0.4.0-lib0.alpha2",
+ "version": "0.4.0-lib0.alpha3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "start-sdk",
- "version": "0.4.0-lib0.alpha2",
+ "version": "0.4.0-lib0.alpha3",
"license": "MIT",
"dependencies": {
"@iarna/toml": "^2.2.5",
diff --git a/package.json b/package.json
index b80653f..0806993 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "start-sdk",
- "version": "0.4.0-lib0.alpha3",
+ "version": "0.4.0-lib0.alpha4",
"description": "For making the patterns that are wanted in making services for the startOS.",
"main": "./index.cjs",
"types": "./index.d.ts",