diff --git a/sdk/package/lib/StartSdk.ts b/sdk/package/lib/StartSdk.ts index 9b3349e83..3c6a05abd 100644 --- a/sdk/package/lib/StartSdk.ts +++ b/sdk/package/lib/StartSdk.ts @@ -229,9 +229,18 @@ export class StartSdk { options: CommandOptions & { mounts?: { path: string; options: MountOptions }[] }, - name: string, + /** + * A name to use to refer to the ephemeral subcontainer for debugging purposes + */ + name?: string, ): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> => { - return runCommand(effects, image, command, options, name) + return runCommand( + effects, + image, + command, + options, + name || (Array.isArray(command) ? command.join(" ") : command), + ) }, /** * @description Use this class to create an Action. By convention, each Action should receive its own file. @@ -686,6 +695,18 @@ export class StartSdk { ) { return SubContainer.of(effects, image, name) }, + with( + effects: T.Effects, + image: { + imageId: T.ImageId & keyof Manifest["images"] + sharedRun?: boolean + }, + mounts: { options: MountOptions; path: string }[], + name: string, + fn: (subContainer: SubContainer) => Promise, + ): Promise { + return SubContainer.with(effects, image, mounts, name, fn) + }, }, List: { /** @@ -700,108 +721,15 @@ export class StartSdk { * @param aSpec - attributes describing each member of the list. */ obj: >( - a: { - name: string - description?: string | null - /** Presents a warning before adding/removing/editing a list item. */ - warning?: string | null - default?: [] - minLength?: number | null - maxLength?: number | null - }, - aSpec: { - spec: InputSpec - /** - * @description The ID of a required field on the inner object whose value will be used to display items in the list. - * @example - * In this example, we use the value of the `label` field to display members of the list. - * - * ``` - spec: InputSpec.of({ - label: Value.text({ - name: 'Label', - required: false, - default: null, - }) - }) - displayAs: 'label', - uniqueBy: null, - * ``` - * - */ - displayAs?: null | string - /** - * @description The ID(s) of required fields on the inner object whose value(s) will be used to enforce uniqueness in the list. - * @example - * In this example, we use the `label` field to enforce uniqueness, meaning the label field must be unique from other entries. - * - * ``` - spec: InputSpec.of({ - label: Value.text({ - name: 'Label', - required: true, - default: null, - }) - pubkey: Value.text({ - name: 'Pubkey', - required: true, - default: null, - }) - }) - displayAs: 'label', - uniqueBy: 'label', - * ``` - * @example - * In this example, we use the `label` field AND the `pubkey` field to enforce uniqueness, meaning both these fields must be unique from other entries. - * - * ``` - spec: InputSpec.of({ - label: Value.text({ - name: 'Label', - required: true, - default: null, - }) - pubkey: Value.text({ - name: 'Pubkey', - required: true, - default: null, - }) - }) - displayAs: 'label', - uniqueBy: { all: ['label', 'pubkey'] }, - * ``` - */ - uniqueBy?: null | UniqueBy - }, + a: Parameters>[0], + aSpec: Parameters>[1], ) => List.obj(a, aSpec), /** * @description Create a list of dynamic text inputs. * @param a - attributes of the list itself. * @param aSpec - attributes describing each member of the list. */ - dynamicText: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - warning?: string | null - default?: string[] - minLength?: number | null - maxLength?: number | null - disabled?: false | string - generate?: null | RandomString - spec: { - masked?: boolean - placeholder?: string | null - minLength?: number | null - maxLength?: number | null - patterns: Pattern[] - inputmode?: ListValueSpecText["inputmode"] - } - } - >, - ) => List.dynamicText(getA), + dynamicText: List.dynamicText, }, StorePath: pathBuilder(), Value: { @@ -1090,244 +1018,14 @@ export class StartSdk { */ list: Value.list, hidden: Value.hidden, - dynamicToggle: ( - a: LazyBuild< - Store, - { - name: string - description?: string | null - /** Presents a warning prompt before permitting the value to change. */ - warning?: string | null - default: boolean - disabled?: false | string - } - >, - ) => Value.dynamicToggle(a), - dynamicText: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - /** Presents a warning prompt before permitting the value to change. */ - warning?: string | null - /** - * @description optionally provide a default value. - * @type { string | RandomString | null } - * @example default: null - * @example default: 'World' - * @example default: { charset: 'abcdefg', len: 16 } - */ - default: DefaultString | null - required: boolean - /** - * @description Mask (aka camouflage) text input with dots: ● ● ● - * @default false - */ - masked?: boolean - placeholder?: string | null - minLength?: number | null - maxLength?: number | null - /** - * @description A list of regular expressions to which the text must conform to pass validation. A human readable description is provided in case the validation fails. - * @default [] - * @example - * ``` - [ - { - regex: "[a-z]", - description: "May only contain lower case letters from the English alphabet." - } - ] - * ``` - */ - patterns?: Pattern[] - /** - * @description Informs the browser how to behave and which keyboard to display on mobile - * @default "text" - */ - inputmode?: ValueSpecText["inputmode"] - /** - * @description Displays a button that will generate a random string according to the provided charset and len attributes. - */ - generate?: null | RandomString - } - >, - ) => Value.dynamicText(getA), - dynamicTextarea: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - /** Presents a warning prompt before permitting the value to change. */ - warning?: string | null - default: string | null - required: boolean - minLength?: number | null - maxLength?: number | null - placeholder?: string | null - disabled?: false | string - } - >, - ) => Value.dynamicTextarea(getA), - dynamicNumber: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - /** Presents a warning prompt before permitting the value to change. */ - warning?: string | null - /** - * @description optionally provide a default value. - * @type { number | null } - * @example default: null - * @example default: 7 - */ - default: number | null - required: boolean - min?: number | null - max?: number | null - /** - * @description How much does the number increase/decrease when using the arrows provided by the browser. - * @default 1 - */ - step?: number | null - /** - * @description Requires the number to be an integer. - */ - integer: boolean - /** - * @description Optionally display units to the right of the input box. - */ - units?: string | null - placeholder?: string | null - disabled?: false | string - } - >, - ) => Value.dynamicNumber(getA), - dynamicColor: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - /** Presents a warning prompt before permitting the value to change. */ - warning?: string | null - /** - * @description optionally provide a default value. - * @type { string | null } - * @example default: null - * @example default: 'ffffff' - */ - default: string | null - required: boolean - disabled?: false | string - } - >, - ) => Value.dynamicColor(getA), - dynamicDatetime: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - /** Presents a warning prompt before permitting the value to change. */ - warning?: string | null - /** - * @description optionally provide a default value. - * @type { string | null } - * @example default: null - * @example default: '1985-12-16 18:00:00.000' - */ - default: string - required: boolean - /** - * @description Informs the browser how to behave and which date/time component to display. - * @default "datetime-local" - */ - inputmode?: ValueSpecDatetime["inputmode"] - min?: string | null - max?: string | null - disabled?: false | string - } - >, - ) => Value.dynamicDatetime(getA), - dynamicSelect: >( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - /** Presents a warning prompt before permitting the value to change. */ - warning?: string | null - /** - * @description provide a default value from the list of values. - * @type { default: string } - * @example default: 'radio1' - */ - default: keyof Variants & string - /** - * @description A mapping of unique radio options to their human readable display format. - * @example - * ``` - { - radio1: "Radio 1" - radio2: "Radio 2" - radio3: "Radio 3" - } - * ``` - */ - values: Variants - /** - * @options - * - false - The field can be modified. - * - string - The field cannot be modified. The provided text explains why. - * - string[] - The field can be modified, but the values contained in the array cannot be selected. - * @default false - */ - disabled?: false | string | string[] - } - >, - ) => Value.dynamicSelect(getA), - dynamicMultiselect: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - /** Presents a warning prompt before permitting the value to change. */ - warning?: string | null - /** - * @description A simple list of which options should be checked by default. - */ - default: string[] - /** - * @description A mapping of checkbox options to their human readable display format. - * @example - * ``` - { - option1: "Option 1" - option2: "Option 2" - option3: "Option 3" - } - * ``` - */ - values: Record - minLength?: number | null - maxLength?: number | null - /** - * @options - * - false - The field can be modified. - * - string - The field cannot be modified. The provided text explains why. - * - string[] - The field can be modified, but the values contained in the array cannot be selected. - * @default false - */ - disabled?: false | string | string[] - } - >, - ) => Value.dynamicMultiselect(getA), + dynamicToggle: Value.dynamicToggle, + dynamicText: Value.dynamicText, + dynamicTextarea: Value.dynamicTextarea, + dynamicNumber: Value.dynamicNumber, + dynamicColor: Value.dynamicColor, + dynamicDatetime: Value.dynamicDatetime, + dynamicSelect: Value.dynamicSelect, + dynamicMultiselect: Value.dynamicMultiselect, filteredUnion: < VariantValues extends { [K in string]: { @@ -1336,16 +1034,13 @@ export class StartSdk { } }, >( - getDisabledFn: LazyBuild, - a: { - name: string - description?: string | null - warning?: string | null - default: keyof VariantValues & string - }, - aVariants: - | Variants - | Variants, + getDisabledFn: Parameters< + typeof Value.filteredUnion + >[0], + a: Parameters>[1], + aVariants: Parameters< + typeof Value.filteredUnion + >[2], ) => Value.filteredUnion( getDisabledFn, @@ -1361,33 +1056,10 @@ export class StartSdk { } }, >( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - /** Presents a warning prompt before permitting the value to change. */ - warning?: string | null - /** - * @description provide a default value from the list of variants. - * @type { string } - * @example default: 'variant1' - */ - default: keyof VariantValues & string - required: boolean - /** - * @options - * - false - The field can be modified. - * - string - The field cannot be modified. The provided text explains why. - * - string[] - The field can be modified, but the values contained in the array cannot be selected. - * @default false - */ - disabled: false | string | string[] - } - >, - aVariants: - | Variants - | Variants, + getA: Parameters>[0], + aVariants: Parameters< + typeof Value.dynamicUnion + >[1], ) => Value.dynamicUnion(getA, aVariants), }, Variants: { diff --git a/sdk/package/lib/util/fileHelper.ts b/sdk/package/lib/util/fileHelper.ts index 0190f838e..74b7b7083 100644 --- a/sdk/package/lib/util/fileHelper.ts +++ b/sdk/package/lib/util/fileHelper.ts @@ -211,24 +211,18 @@ export class FileHelper { } /** - * Accepts full structured data and performs a merge with the existing file on disk if it exists. + * Accepts full structured data and overwrites the existing file on disk if it exists. */ async write(data: A) { - const fileData = (await this.readFile()) || {} - const mergeData = fileMerge({}, fileData, data) - return await this.writeFile(this.validate(mergeData)) + return await this.writeFile(this.validate(data)) } /** * Accepts partial structured data and performs a merge with the existing file on disk. */ async merge(data: T.DeepPartial) { - const fileData = - (await this.readFile()) || - (() => { - throw new Error(`${this.path}: does not exist`) - })() - const mergeData = fileMerge({}, fileData, data) + const fileData = (await this.readFile()) || null + const mergeData = fileMerge(fileData, data) return await this.writeFile(this.validate(mergeData)) } diff --git a/sdk/package/package-lock.json b/sdk/package/package-lock.json index 555599d81..72a4a12a2 100644 --- a/sdk/package/package-lock.json +++ b/sdk/package/package-lock.json @@ -1,12 +1,12 @@ { "name": "@start9labs/start-sdk", - "version": "0.3.6-beta.6", + "version": "0.3.6-beta.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@start9labs/start-sdk", - "version": "0.3.6-beta.6", + "version": "0.3.6-beta.9", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", diff --git a/sdk/package/package.json b/sdk/package/package.json index abc7bb5fb..429164006 100644 --- a/sdk/package/package.json +++ b/sdk/package/package.json @@ -1,6 +1,6 @@ { "name": "@start9labs/start-sdk", - "version": "0.3.6-beta.6", + "version": "0.3.6-beta.9", "description": "Software development kit to facilitate packaging services for StartOS", "main": "./package/lib/index.js", "types": "./package/lib/index.d.ts",