mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
Convert properties to an action (#2751)
* update actions response types and partially implement in UI * further remove diagnostic ui * convert action response nested to array * prepare action res modal for Alex * ad dproperties action for Bitcoin * feat: add action success dialog (#2753) * feat: add action success dialog * mocks for string action res and hide properties from actions page --------- Co-authored-by: Matt Hill <mattnine@protonmail.com> * return null * remove properties from backend * misc fixes * make severity separate argument * rename ActionRequest to ActionRequestOptions * add clearRequests * fix s9pk build * remove config and properties, introduce action requests * better ux, better moocks, include icons * fix dependency types * add variant for versionCompat * fix dep icon display and patch operation display * misc fixes * misc fixes * alpha 12 * honor provided input to set values in action * fix: show full descriptions of action success items (#2758) * fix type * fix: fix build:deps command on Windows (#2752) * fix: fix build:deps command on Windows * fix: add escaped quotes --------- Co-authored-by: Aiden McClelland <me@drbonez.dev> * misc db compatibility fixes --------- Co-authored-by: Alex Inkin <alexander@inkin.ru> Co-authored-by: Aiden McClelland <me@drbonez.dev> Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com>
This commit is contained in:
@@ -55,7 +55,6 @@ import { getStore } from "./store/getStore"
|
||||
import { CommandOptions, MountOptions, SubContainer } from "./util/SubContainer"
|
||||
import { splitCommand } from "./util"
|
||||
import { Mounts } from "./mainFn/Mounts"
|
||||
import { Dependency } from "../../base/lib/dependencies/Dependency"
|
||||
import { setupDependencies } from "../../base/lib/dependencies/setupDependencies"
|
||||
import * as T from "../../base/lib/types"
|
||||
import { testTypeVersion } from "../../base/lib/exver"
|
||||
@@ -86,12 +85,12 @@ type AnyNeverCond<T extends any[], Then, Else> =
|
||||
T extends [any, ...infer U] ? AnyNeverCond<U,Then, Else> :
|
||||
never
|
||||
|
||||
export class StartSdk<Manifest extends T.Manifest, Store> {
|
||||
export class StartSdk<Manifest extends T.SDKManifest, Store> {
|
||||
private constructor(readonly manifest: Manifest) {}
|
||||
static of() {
|
||||
return new StartSdk<never, never>(null as never)
|
||||
}
|
||||
withManifest<Manifest extends T.Manifest = never>(manifest: Manifest) {
|
||||
withManifest<Manifest extends T.SDKManifest = never>(manifest: Manifest) {
|
||||
return new StartSdk<Manifest, Store>(manifest)
|
||||
}
|
||||
withStore<Store extends Record<string, any>>() {
|
||||
@@ -141,17 +140,39 @@ export class StartSdk<Manifest extends T.Manifest, Store> {
|
||||
...startSdkEffectWrapper,
|
||||
action: {
|
||||
run: actions.runAction,
|
||||
request: actions.requestAction,
|
||||
requestOwn: <T extends Omit<T.ActionRequest, "packageId">>(
|
||||
request: <
|
||||
T extends Action<T.ActionId, any, any, Record<string, unknown>>,
|
||||
>(
|
||||
effects: T.Effects,
|
||||
request: actions.ActionRequest<T> & {
|
||||
replayId?: string
|
||||
},
|
||||
packageId: T.PackageId,
|
||||
action: T,
|
||||
severity: T.ActionSeverity,
|
||||
options?: actions.ActionRequestOptions<T>,
|
||||
) =>
|
||||
actions.requestAction({
|
||||
effects,
|
||||
request: { ...request, packageId: this.manifest.id },
|
||||
packageId,
|
||||
action,
|
||||
severity,
|
||||
options: options,
|
||||
}),
|
||||
requestOwn: <
|
||||
T extends Action<T.ActionId, Store, any, Record<string, unknown>>,
|
||||
>(
|
||||
effects: T.Effects,
|
||||
action: T,
|
||||
severity: T.ActionSeverity,
|
||||
options?: actions.ActionRequestOptions<T>,
|
||||
) =>
|
||||
actions.requestAction({
|
||||
effects,
|
||||
packageId: this.manifest.id,
|
||||
action,
|
||||
severity,
|
||||
options: options,
|
||||
}),
|
||||
clearRequest: (effects: T.Effects, ...replayIds: string[]) =>
|
||||
effects.action.clearRequests({ only: replayIds }),
|
||||
},
|
||||
checkDependencies: checkDependencies as <
|
||||
DependencyId extends keyof Manifest["dependencies"] &
|
||||
@@ -370,17 +391,6 @@ export class StartSdk<Manifest extends T.Manifest, Store> {
|
||||
return healthCheck(o)
|
||||
},
|
||||
},
|
||||
Dependency: {
|
||||
/**
|
||||
* @description Use this function to create a dependency for the service.
|
||||
* @property {DependencyType} type
|
||||
* @property {VersionRange} versionRange
|
||||
* @property {string[]} healthChecks
|
||||
*/
|
||||
of(data: Dependency["data"]) {
|
||||
return new Dependency({ ...data })
|
||||
},
|
||||
},
|
||||
healthCheck: {
|
||||
checkPortListening,
|
||||
checkWebUrl,
|
||||
@@ -566,37 +576,6 @@ export class StartSdk<Manifest extends T.Manifest, Store> {
|
||||
started(onTerm: () => PromiseLike<void>): PromiseLike<null>
|
||||
}) => Promise<Daemons<Manifest, any>>,
|
||||
) => setupMain<Manifest, Store>(fn),
|
||||
/**
|
||||
* @description Use this function to determine which information to expose to the UI in the "Properties" section.
|
||||
*
|
||||
* Values can be obtained from anywhere: the Store, the upstream service, or another service.
|
||||
* @example
|
||||
* In this example, we retrieve the admin password from the Store and expose it, masked and copyable, to
|
||||
* the UI as "Admin Password".
|
||||
*
|
||||
* ```
|
||||
export const properties = sdk.setupProperties(async ({ effects }) => {
|
||||
const store = await sdk.store.getOwn(effects, sdk.StorePath).once()
|
||||
|
||||
return {
|
||||
'Admin Password': {
|
||||
type: 'string',
|
||||
value: store.adminPassword,
|
||||
description: 'Used for logging into the admin UI',
|
||||
copyable: true,
|
||||
masked: true,
|
||||
qr: false,
|
||||
},
|
||||
}
|
||||
})
|
||||
* ```
|
||||
*/
|
||||
setupProperties:
|
||||
(
|
||||
fn: (options: { effects: Effects }) => Promise<T.SdkPropertiesReturn>,
|
||||
): T.ExpectedExports.properties =>
|
||||
(options) =>
|
||||
fn(options).then(nullifyProperties),
|
||||
/**
|
||||
* Use this function to execute arbitrary logic *once*, on uninstall only. Most services will not use this.
|
||||
*/
|
||||
@@ -1057,6 +1036,7 @@ export class StartSdk<Manifest extends T.Manifest, Store> {
|
||||
* ```
|
||||
*/
|
||||
list: Value.list,
|
||||
hidden: Value.hidden,
|
||||
dynamicToggle: (
|
||||
a: LazyBuild<
|
||||
Store,
|
||||
@@ -1367,7 +1347,7 @@ export class StartSdk<Manifest extends T.Manifest, Store> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function runCommand<Manifest extends T.Manifest>(
|
||||
export async function runCommand<Manifest extends T.SDKManifest>(
|
||||
effects: Effects,
|
||||
image: { id: keyof Manifest["images"] & T.ImageId; sharedRun?: boolean },
|
||||
command: string | [string, ...string[]],
|
||||
@@ -1385,26 +1365,3 @@ export async function runCommand<Manifest extends T.Manifest>(
|
||||
(subcontainer) => subcontainer.exec(commands),
|
||||
)
|
||||
}
|
||||
function nullifyProperties(value: T.SdkPropertiesReturn): T.PropertiesReturn {
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).map(([k, v]) => [k, nullifyProperties_(v)]),
|
||||
)
|
||||
}
|
||||
function nullifyProperties_(value: T.SdkPropertiesValue): T.PropertiesValue {
|
||||
if (value.type === "string") {
|
||||
return {
|
||||
description: null,
|
||||
copyable: null,
|
||||
masked: null,
|
||||
qr: null,
|
||||
...value,
|
||||
}
|
||||
}
|
||||
return {
|
||||
description: null,
|
||||
...value,
|
||||
value: Object.fromEntries(
|
||||
Object.entries(value.value).map(([k, v]) => [k, nullifyProperties_(v)]),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export type BackupSync<Volumes extends string> = {
|
||||
* ).build()q
|
||||
* ```
|
||||
*/
|
||||
export class Backups<M extends T.Manifest> {
|
||||
export class Backups<M extends T.SDKManifest> {
|
||||
private constructor(
|
||||
private options = DEFAULT_OPTIONS,
|
||||
private restoreOptions: Partial<T.SyncOptions> = {},
|
||||
@@ -43,7 +43,7 @@ export class Backups<M extends T.Manifest> {
|
||||
private backupSet = [] as BackupSync<M["volumes"][number]>[],
|
||||
) {}
|
||||
|
||||
static withVolumes<M extends T.Manifest = never>(
|
||||
static withVolumes<M extends T.SDKManifest = never>(
|
||||
...volumeNames: Array<M["volumes"][number]>
|
||||
): Backups<M> {
|
||||
return Backups.withSyncs(
|
||||
@@ -54,13 +54,13 @@ export class Backups<M extends T.Manifest> {
|
||||
)
|
||||
}
|
||||
|
||||
static withSyncs<M extends T.Manifest = never>(
|
||||
static withSyncs<M extends T.SDKManifest = never>(
|
||||
...syncs: BackupSync<M["volumes"][number]>[]
|
||||
) {
|
||||
return syncs.reduce((acc, x) => acc.addSync(x), new Backups<M>())
|
||||
}
|
||||
|
||||
static withOptions<M extends T.Manifest = never>(
|
||||
static withOptions<M extends T.SDKManifest = never>(
|
||||
options?: Partial<T.SyncOptions>,
|
||||
) {
|
||||
return new Backups<M>({ ...DEFAULT_OPTIONS, ...options })
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Backups } from "./Backups"
|
||||
import * as T from "../../../base/lib/types"
|
||||
import { _ } from "../util"
|
||||
|
||||
export type SetupBackupsParams<M extends T.Manifest> =
|
||||
export type SetupBackupsParams<M extends T.SDKManifest> =
|
||||
| M["volumes"][number][]
|
||||
| ((_: { effects: T.Effects }) => Promise<Backups<M>>)
|
||||
|
||||
@@ -11,7 +11,7 @@ type SetupBackupsRes = {
|
||||
restoreBackup: T.ExpectedExports.restoreBackup
|
||||
}
|
||||
|
||||
export function setupBackups<M extends T.Manifest>(
|
||||
export function setupBackups<M extends T.SDKManifest>(
|
||||
options: SetupBackupsParams<M>,
|
||||
) {
|
||||
let backupsFactory: (_: { effects: T.Effects }) => Promise<Backups<M>>
|
||||
|
||||
@@ -28,7 +28,7 @@ export {
|
||||
export { Daemons } from "./mainFn/Daemons"
|
||||
export { SubContainer } from "./util/SubContainer"
|
||||
export { StartSdk } from "./StartSdk"
|
||||
export { setupManifest } from "./manifest/setupManifest"
|
||||
export { setupManifest, buildManifest } from "./manifest/setupManifest"
|
||||
export { FileHelper } from "./util/fileHelper"
|
||||
export { setupExposeStore } from "./store/setupExposeStore"
|
||||
export { pathBuilder } from "../../base/lib/util/PathBuilder"
|
||||
|
||||
@@ -7,12 +7,14 @@ import { VersionGraph } from "../version/VersionGraph"
|
||||
import { Install } from "./setupInstall"
|
||||
import { Uninstall } from "./setupUninstall"
|
||||
|
||||
export function setupInit<Manifest extends T.Manifest, Store>(
|
||||
versions: VersionGraph<Manifest["version"]>,
|
||||
export function setupInit<Manifest extends T.SDKManifest, Store>(
|
||||
versions: VersionGraph<string>,
|
||||
install: Install<Manifest, Store>,
|
||||
uninstall: Uninstall<Manifest, Store>,
|
||||
setServiceInterfaces: UpdateServiceInterfaces<any>,
|
||||
setDependencies: (options: { effects: T.Effects }) => Promise<null>,
|
||||
setDependencies: (options: {
|
||||
effects: T.Effects
|
||||
}) => Promise<null | void | undefined>,
|
||||
actions: Actions<Store, any>,
|
||||
exposedStore: ExposedStorePaths,
|
||||
): {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import * as T from "../../../base/lib/types"
|
||||
|
||||
export type InstallFn<Manifest extends T.Manifest, Store> = (opts: {
|
||||
export type InstallFn<Manifest extends T.SDKManifest, Store> = (opts: {
|
||||
effects: T.Effects
|
||||
}) => Promise<null>
|
||||
export class Install<Manifest extends T.Manifest, Store> {
|
||||
}) => Promise<null | void | undefined>
|
||||
export class Install<Manifest extends T.SDKManifest, Store> {
|
||||
private constructor(readonly fn: InstallFn<Manifest, Store>) {}
|
||||
static of<Manifest extends T.Manifest, Store>(
|
||||
static of<Manifest extends T.SDKManifest, Store>(
|
||||
fn: InstallFn<Manifest, Store>,
|
||||
) {
|
||||
return new Install(fn)
|
||||
@@ -18,7 +18,7 @@ export class Install<Manifest extends T.Manifest, Store> {
|
||||
}
|
||||
}
|
||||
|
||||
export function setupInstall<Manifest extends T.Manifest, Store>(
|
||||
export function setupInstall<Manifest extends T.SDKManifest, Store>(
|
||||
fn: InstallFn<Manifest, Store>,
|
||||
) {
|
||||
return Install.of(fn)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import * as T from "../../../base/lib/types"
|
||||
|
||||
export type UninstallFn<Manifest extends T.Manifest, Store> = (opts: {
|
||||
export type UninstallFn<Manifest extends T.SDKManifest, Store> = (opts: {
|
||||
effects: T.Effects
|
||||
}) => Promise<null>
|
||||
export class Uninstall<Manifest extends T.Manifest, Store> {
|
||||
}) => Promise<null | void | undefined>
|
||||
export class Uninstall<Manifest extends T.SDKManifest, Store> {
|
||||
private constructor(readonly fn: UninstallFn<Manifest, Store>) {}
|
||||
static of<Manifest extends T.Manifest, Store>(
|
||||
static of<Manifest extends T.SDKManifest, Store>(
|
||||
fn: UninstallFn<Manifest, Store>,
|
||||
) {
|
||||
return new Uninstall(fn)
|
||||
@@ -22,7 +22,7 @@ export class Uninstall<Manifest extends T.Manifest, Store> {
|
||||
}
|
||||
}
|
||||
|
||||
export function setupUninstall<Manifest extends T.Manifest, Store>(
|
||||
export function setupUninstall<Manifest extends T.SDKManifest, Store>(
|
||||
fn: UninstallFn<Manifest, Store>,
|
||||
) {
|
||||
return Uninstall.of(fn)
|
||||
|
||||
@@ -20,7 +20,7 @@ export class CommandController {
|
||||
private process: cp.ChildProcessWithoutNullStreams,
|
||||
readonly sigtermTimeout: number = DEFAULT_SIGTERM_TIMEOUT,
|
||||
) {}
|
||||
static of<Manifest extends T.Manifest>() {
|
||||
static of<Manifest extends T.SDKManifest>() {
|
||||
return async <A extends string>(
|
||||
effects: T.Effects,
|
||||
subcontainer:
|
||||
|
||||
@@ -17,7 +17,7 @@ export class Daemon {
|
||||
get subContainerHandle(): undefined | ExecSpawnable {
|
||||
return this.commandController?.subContainerHandle
|
||||
}
|
||||
static of<Manifest extends T.Manifest>() {
|
||||
static of<Manifest extends T.SDKManifest>() {
|
||||
return async <A extends string>(
|
||||
effects: T.Effects,
|
||||
subcontainer:
|
||||
|
||||
@@ -27,7 +27,7 @@ export type Ready = {
|
||||
}
|
||||
|
||||
type DaemonsParams<
|
||||
Manifest extends T.Manifest,
|
||||
Manifest extends T.SDKManifest,
|
||||
Ids extends string,
|
||||
Command extends string,
|
||||
Id extends string,
|
||||
@@ -43,7 +43,7 @@ type DaemonsParams<
|
||||
|
||||
type ErrorDuplicateId<Id extends string> = `The id '${Id}' is already used`
|
||||
|
||||
export const runCommand = <Manifest extends T.Manifest>() =>
|
||||
export const runCommand = <Manifest extends T.SDKManifest>() =>
|
||||
CommandController.of<Manifest>()
|
||||
|
||||
/**
|
||||
@@ -69,7 +69,7 @@ Daemons.of({
|
||||
})
|
||||
```
|
||||
*/
|
||||
export class Daemons<Manifest extends T.Manifest, Ids extends string>
|
||||
export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
implements T.DaemonBuildable
|
||||
{
|
||||
private constructor(
|
||||
@@ -89,7 +89,7 @@ export class Daemons<Manifest extends T.Manifest, Ids extends string>
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
static of<Manifest extends T.Manifest>(options: {
|
||||
static of<Manifest extends T.SDKManifest>(options: {
|
||||
effects: T.Effects
|
||||
started: (onTerm: () => PromiseLike<void>) => PromiseLike<null>
|
||||
healthReceipts: HealthReceipt[]
|
||||
|
||||
@@ -3,7 +3,7 @@ import { MountOptions } from "../util/SubContainer"
|
||||
|
||||
type MountArray = { path: string; options: MountOptions }[]
|
||||
|
||||
export class Mounts<Manifest extends T.Manifest> {
|
||||
export class Mounts<Manifest extends T.SDKManifest> {
|
||||
private constructor(
|
||||
readonly volumes: {
|
||||
id: Manifest["volumes"][number]
|
||||
@@ -25,7 +25,7 @@ export class Mounts<Manifest extends T.Manifest> {
|
||||
}[],
|
||||
) {}
|
||||
|
||||
static of<Manifest extends T.Manifest>() {
|
||||
static of<Manifest extends T.SDKManifest>() {
|
||||
return new Mounts<Manifest>([], [], [])
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export class Mounts<Manifest extends T.Manifest> {
|
||||
return this
|
||||
}
|
||||
|
||||
addDependency<DependencyManifest extends T.Manifest>(
|
||||
addDependency<DependencyManifest extends T.SDKManifest>(
|
||||
dependencyId: keyof Manifest["dependencies"] & string,
|
||||
volumeId: DependencyManifest["volumes"][number],
|
||||
subpath: string | null,
|
||||
|
||||
@@ -14,7 +14,7 @@ export const DEFAULT_SIGTERM_TIMEOUT = 30_000
|
||||
* @param fn
|
||||
* @returns
|
||||
*/
|
||||
export const setupMain = <Manifest extends T.Manifest, Store>(
|
||||
export const setupMain = <Manifest extends T.SDKManifest, Store>(
|
||||
fn: (o: {
|
||||
effects: T.Effects
|
||||
started(onTerm: () => PromiseLike<void>): PromiseLike<null>
|
||||
|
||||
@@ -14,6 +14,23 @@ import { VersionGraph } from "../version/VersionGraph"
|
||||
* @param manifest Static properties of the package
|
||||
*/
|
||||
export function setupManifest<
|
||||
Id extends string,
|
||||
Dependencies extends Record<string, unknown>,
|
||||
VolumesTypes extends VolumeId,
|
||||
AssetTypes extends VolumeId,
|
||||
ImagesTypes extends ImageId,
|
||||
Manifest extends {
|
||||
dependencies: Dependencies
|
||||
id: Id
|
||||
assets: AssetTypes[]
|
||||
images: Record<ImagesTypes, SDKImageInputSpec>
|
||||
volumes: VolumesTypes[]
|
||||
},
|
||||
>(manifest: SDKManifest & Manifest): SDKManifest & Manifest {
|
||||
return manifest
|
||||
}
|
||||
|
||||
export function buildManifest<
|
||||
Id extends string,
|
||||
Version extends string,
|
||||
Dependencies extends Record<string, unknown>,
|
||||
@@ -27,7 +44,6 @@ export function setupManifest<
|
||||
images: Record<ImagesTypes, SDKImageInputSpec>
|
||||
volumes: VolumesTypes[]
|
||||
},
|
||||
Satisfies extends string[] = [],
|
||||
>(
|
||||
versions: VersionGraph<Version>,
|
||||
manifest: SDKManifest & Manifest,
|
||||
|
||||
@@ -367,48 +367,39 @@ describe("values", () => {
|
||||
test("datetime", async () => {
|
||||
const sdk = StartSdk.of()
|
||||
.withManifest(
|
||||
setupManifest(
|
||||
VersionGraph.of(
|
||||
VersionInfo.of({
|
||||
version: "1.0.0:0",
|
||||
releaseNotes: "",
|
||||
migrations: {},
|
||||
}),
|
||||
),
|
||||
{
|
||||
id: "testOutput",
|
||||
title: "",
|
||||
license: "",
|
||||
wrapperRepo: "",
|
||||
upstreamRepo: "",
|
||||
supportSite: "",
|
||||
marketingSite: "",
|
||||
donationUrl: null,
|
||||
description: {
|
||||
short: "",
|
||||
long: "",
|
||||
},
|
||||
containers: {},
|
||||
images: {},
|
||||
volumes: [],
|
||||
assets: [],
|
||||
alerts: {
|
||||
install: null,
|
||||
update: null,
|
||||
uninstall: null,
|
||||
restore: null,
|
||||
start: null,
|
||||
stop: null,
|
||||
},
|
||||
dependencies: {
|
||||
"remote-test": {
|
||||
description: "",
|
||||
optional: true,
|
||||
s9pk: "https://example.com/remote-test.s9pk",
|
||||
},
|
||||
setupManifest({
|
||||
id: "testOutput",
|
||||
title: "",
|
||||
license: "",
|
||||
wrapperRepo: "",
|
||||
upstreamRepo: "",
|
||||
supportSite: "",
|
||||
marketingSite: "",
|
||||
donationUrl: null,
|
||||
description: {
|
||||
short: "",
|
||||
long: "",
|
||||
},
|
||||
containers: {},
|
||||
images: {},
|
||||
volumes: [],
|
||||
assets: [],
|
||||
alerts: {
|
||||
install: null,
|
||||
update: null,
|
||||
uninstall: null,
|
||||
restore: null,
|
||||
start: null,
|
||||
stop: null,
|
||||
},
|
||||
dependencies: {
|
||||
"remote-test": {
|
||||
description: "",
|
||||
optional: true,
|
||||
s9pk: "https://example.com/remote-test.s9pk",
|
||||
},
|
||||
},
|
||||
),
|
||||
}),
|
||||
)
|
||||
.withStore<{ test: "a" }>()
|
||||
.build(true)
|
||||
|
||||
@@ -6,51 +6,40 @@ import { VersionGraph } from "../version/VersionGraph"
|
||||
export type Manifest = any
|
||||
export const sdk = StartSdk.of()
|
||||
.withManifest(
|
||||
setupManifest(
|
||||
VersionGraph.of(
|
||||
VersionInfo.of({
|
||||
version: "1.0.0:0",
|
||||
releaseNotes: "",
|
||||
migrations: {},
|
||||
})
|
||||
.satisfies("#other:1.0.0:0")
|
||||
.satisfies("#other:2.0.0:0"),
|
||||
),
|
||||
{
|
||||
id: "testOutput",
|
||||
title: "",
|
||||
license: "",
|
||||
replaces: [],
|
||||
wrapperRepo: "",
|
||||
upstreamRepo: "",
|
||||
supportSite: "",
|
||||
marketingSite: "",
|
||||
donationUrl: null,
|
||||
description: {
|
||||
short: "",
|
||||
long: "",
|
||||
},
|
||||
containers: {},
|
||||
images: {},
|
||||
volumes: [],
|
||||
assets: [],
|
||||
alerts: {
|
||||
install: null,
|
||||
update: null,
|
||||
uninstall: null,
|
||||
restore: null,
|
||||
start: null,
|
||||
stop: null,
|
||||
},
|
||||
dependencies: {
|
||||
"remote-test": {
|
||||
description: "",
|
||||
optional: false,
|
||||
s9pk: "https://example.com/remote-test.s9pk",
|
||||
},
|
||||
setupManifest({
|
||||
id: "testOutput",
|
||||
title: "",
|
||||
license: "",
|
||||
replaces: [],
|
||||
wrapperRepo: "",
|
||||
upstreamRepo: "",
|
||||
supportSite: "",
|
||||
marketingSite: "",
|
||||
donationUrl: null,
|
||||
description: {
|
||||
short: "",
|
||||
long: "",
|
||||
},
|
||||
containers: {},
|
||||
images: {},
|
||||
volumes: [],
|
||||
assets: [],
|
||||
alerts: {
|
||||
install: null,
|
||||
update: null,
|
||||
uninstall: null,
|
||||
restore: null,
|
||||
start: null,
|
||||
stop: null,
|
||||
},
|
||||
dependencies: {
|
||||
"remote-test": {
|
||||
description: "",
|
||||
optional: false,
|
||||
s9pk: "https://example.com/remote-test.s9pk",
|
||||
},
|
||||
},
|
||||
),
|
||||
}),
|
||||
)
|
||||
.withStore<{ storeRoot: { storeLeaf: "value" } }>()
|
||||
.build(true)
|
||||
|
||||
@@ -86,19 +86,21 @@ export class FileHelper<A> {
|
||||
/**
|
||||
* Accepts structured data and overwrites the existing file on disk.
|
||||
*/
|
||||
async write(data: A) {
|
||||
async write(data: A): Promise<null> {
|
||||
const parent = previousPath.exec(this.path)
|
||||
if (parent) {
|
||||
await fs.mkdir(parent[1], { recursive: true })
|
||||
}
|
||||
|
||||
await fs.writeFile(this.path, this.writeData(data))
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the file from disk and converts it to structured data.
|
||||
*/
|
||||
async read() {
|
||||
private async readOnce(): Promise<A | null> {
|
||||
if (!(await exists(this.path))) {
|
||||
return null
|
||||
}
|
||||
@@ -107,14 +109,14 @@ export class FileHelper<A> {
|
||||
)
|
||||
}
|
||||
|
||||
async const(effects: T.Effects) {
|
||||
const watch = this.watch()
|
||||
private async readConst(effects: T.Effects): Promise<A | null> {
|
||||
const watch = this.readWatch()
|
||||
const res = await watch.next()
|
||||
watch.next().then(effects.constRetry)
|
||||
return res.value
|
||||
}
|
||||
|
||||
async *watch() {
|
||||
private async *readWatch() {
|
||||
let res
|
||||
while (true) {
|
||||
if (await exists(this.path)) {
|
||||
@@ -123,12 +125,12 @@ export class FileHelper<A> {
|
||||
persistent: false,
|
||||
signal: ctrl.signal,
|
||||
})
|
||||
res = await this.read()
|
||||
res = await this.readOnce()
|
||||
const listen = Promise.resolve()
|
||||
.then(async () => {
|
||||
for await (const _ of watch) {
|
||||
ctrl.abort("finished")
|
||||
return
|
||||
return null
|
||||
}
|
||||
})
|
||||
.catch((e) => console.error(asError(e)))
|
||||
@@ -139,13 +141,22 @@ export class FileHelper<A> {
|
||||
await onCreated(this.path).catch((e) => console.error(asError(e)))
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
get read() {
|
||||
return {
|
||||
once: () => this.readOnce(),
|
||||
const: (effects: T.Effects) => this.readConst(effects),
|
||||
watch: () => this.readWatch(),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts structured data and performs a merge with the existing file on disk.
|
||||
*/
|
||||
async merge(data: A) {
|
||||
const fileData = (await this.read().catch(() => ({}))) || {}
|
||||
const fileData = (await this.readOnce().catch(() => ({}))) || {}
|
||||
const mergeData = merge({}, fileData, data)
|
||||
return await this.write(mergeData)
|
||||
}
|
||||
|
||||
33
sdk/package/package-lock.json
generated
33
sdk/package/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.3.6-alpha8",
|
||||
"version": "0.3.6-alpha9",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.3.6-alpha8",
|
||||
"version": "0.3.6-alpha9",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@iarna/toml": "^2.2.5",
|
||||
@@ -14,7 +14,7 @@
|
||||
"@noble/hashes": "^1.4.0",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"mime": "^4.0.3",
|
||||
"mime-types": "^2.1.35",
|
||||
"ts-matches": "^5.5.1",
|
||||
"yaml": "^2.2.2"
|
||||
},
|
||||
@@ -3136,18 +3136,25 @@
|
||||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-4.0.3.tgz",
|
||||
"integrity": "sha512-KgUb15Oorc0NEKPbvfa0wRU+PItIEZmiv+pyAO2i0oTIVTJhlzMclU7w4RXWQrSOVH5ax/p/CkIO7KI4OyFJTQ==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa"
|
||||
],
|
||||
"bin": {
|
||||
"mime": "bin/cli.js"
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-fn": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@start9labs/start-sdk",
|
||||
"version": "0.3.6-alpha8",
|
||||
"version": "0.3.6-alpha.12",
|
||||
"description": "Software development kit to facilitate packaging services for StartOS",
|
||||
"main": "./package/lib/index.js",
|
||||
"types": "./package/lib/index.d.ts",
|
||||
@@ -15,7 +15,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jest -c ./jest.config.js --coverage",
|
||||
"buildOutput": "ts-node ./lib/test/makeOutput.ts && npx prettier --write '**/*.ts'",
|
||||
"buildOutput": "ts-node ./lib/test/makeOutput.ts && npx prettier --write \"**/*.ts\"",
|
||||
"check": "tsc --noEmit",
|
||||
"tsc": "tsc"
|
||||
},
|
||||
@@ -32,7 +32,7 @@
|
||||
"dependencies": {
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"mime": "^4.0.3",
|
||||
"mime-types": "^2.1.35",
|
||||
"ts-matches": "^5.5.1",
|
||||
"yaml": "^2.2.2",
|
||||
"@iarna/toml": "^2.2.5",
|
||||
|
||||
Reference in New Issue
Block a user