Be specific instead of any in Inquire and SchemaData

main
Mari 3 years ago
parent 68593b1cf0
commit d3d820e60a
  1. 12
      src/commands/AddEntry.ts
  2. 6
      src/prompts/Inquire.ts
  3. 2
      src/prompts/implementations/ConditionPrompt.ts
  4. 3
      src/prompts/implementations/EmpathyGroupListPrompt.ts
  5. 5
      src/prompts/implementations/EmpathyGroupPrompt.ts
  6. 6
      src/prompts/implementations/EmpathyGuidePrompt.ts
  7. 14
      src/prompts/implementations/EntryMainMenuPrompt.ts
  8. 4
      src/prompts/implementations/GuidedEmpathyListPrompt.ts
  9. 4
      src/prompts/implementations/GuidedEmpathyPrompt.ts
  10. 2
      src/prompts/implementations/JournalEntryPrompt.ts
  11. 2
      src/prompts/implementations/SuicidalityPrompt.ts
  12. 2
      src/prompts/implementations/SummaryPrompt.ts
  13. 33
      src/prompts/types/HierarchicalCheckboxInput.ts
  14. 6
      src/prompts/types/MultiTextInput.ts
  15. 21
      src/repository/LocalRepository.ts
  16. 46
      src/schemata/SchemaData.ts
  17. 15
      src/schemata/YAMLPrompt.ts
  18. 2
      src/utils/Merge.ts

@ -33,30 +33,41 @@ export function addEntryCommand(): CommandModule {
const empathyList = guidedEmpathyListPrompt({inquire, promptForEmpathy: empathy})
const mainMenuItems = [
// TODO: add ability to amend previous entry
condition.mainMenu,
summary.mainMenu,
new Separator(),
journal.mainMenu,
empathyList.mainMenu,
// TODO: Modified HierarchicalCheckboxInput to allow for putting more things in the main menu than
// there are letters
// TODO: gratitude journaling
// TODO: thinking about the future - where do you want to be in x years type thing
// TODO: breathing regulation exercises
// TODO: Goals for the day and checking in on how yesterday's goals went
/*
{
name: typeof entry.needs === "object" ? "Check back in on needs" : "Check in on needs",
// TODO: physical needs: food, water, sleep, exercise tracking, possibly with autocompletes for each saved in a file like the empathy guide
value: NEEDS,
key: "n"
},
{
name: typeof entry.personas === "object" ? "Check back in on personas" : "Check in on personas",
// TODO: Personas' individual journal entries
value: PERSONA,
key: "p"
},
{name: typeof entry.rpg === "object" ? "Change RPG stats" : "Add RPG stats", value: RPG, key: "r"},
{
name: typeof entry.activities === "object" ? "Change record of recent activities" : "Add record of recent activities",
// TODO: Add general activity set with historical activities saved in a file like the empathy guide
value: ACTIVITIES,
key: "a"
},
{
name: typeof entry.music === "object" ? "Change record of recently played music" : "Add record of recently played music",
// TODO: Add music
value: MUSIC,
key: "m"
},
@ -65,6 +76,7 @@ export function addEntryCommand(): CommandModule {
/*
{
name: typeof entry.recoveries === "object" ? "Try more recovery methods" : "Try some recovery methods",
// TODO: Add list of recovery methods
value: RECOVERIES,
key: "y"
},

@ -1,9 +1,9 @@
import {prompt, QuestionCollection} from "inquirer";
import {prompt, QuestionMap} from "inquirer";
export type InquireFunction = (question: Omit<QuestionCollection, "name">) => Promise<any>
export type InquireFunction<QuestionT extends QuestionMap[keyof QuestionMap], AnswerT extends QuestionT["default"]> = (question: QuestionT) => Promise<AnswerT>
export type ShowFunction = (text: string) => Promise<void>
export async function inquire(question: Omit<QuestionCollection, "name">): Promise<any> {
export async function inquire<QuestionT extends QuestionMap[keyof QuestionMap], AnswerT extends QuestionT["default"]>(question: QuestionT): Promise<AnswerT> {
const result = await prompt([{...question, name: "answer"}])
return result.answer
}

@ -10,7 +10,7 @@ export interface ConditionPromptOptions extends Partial<Omit<ListQuestion, "name
}
export interface ConditionPromptDependencies {
readonly inquire: InquireFunction
readonly inquire: InquireFunction<ListQuestion, Condition>
}
export function conditionPrompt(deps: ConditionPromptDependencies): {

@ -4,6 +4,7 @@ import chalk from "chalk";
import pluralize from "pluralize";
import {InquireFunction} from "../Inquire";
import {EmpathyGroupPromptOptions} from "./EmpathyGroupPrompt";
import {ListQuestion} from "inquirer";
export interface EmpathyGroupListPromptOptions {
readonly default?: readonly EmpathyGroup[]
@ -11,7 +12,7 @@ export interface EmpathyGroupListPromptOptions {
}
export interface EmpathyGroupListPromptDependencies {
readonly inquire: InquireFunction
readonly inquire: InquireFunction<ListQuestion, number>
readonly promptForEmpathyGroup: (opts: EmpathyGroupPromptOptions) => Promise<EmpathyGroup | null>
}

@ -1,8 +1,9 @@
import capitalize from "capitalize";
import {editYaml} from "../../schemata/YAMLPrompt";
import {editYaml, EditYamlInquire} from "../../schemata/YAMLPrompt";
import chalk from "chalk";
import {InquireFunction, ShowFunction} from "../Inquire";
import {EmpathyGroup, EmpathyGroupJTD} from "../../datatypes/EmpathyGroup";
import {ExpandQuestion, InputQuestion, MultiTextInputQuestion} from "inquirer";
export interface EmpathyGroupPromptOptions {
default?: EmpathyGroup
@ -10,7 +11,7 @@ export interface EmpathyGroupPromptOptions {
}
export interface EmpathyGroupPromptDependencies {
inquire: InquireFunction
inquire: InquireFunction<ExpandQuestion, typeof PROMPT|typeof AUTO|typeof YAML> & EditYamlInquire & InquireFunction<InputQuestion, string> & InquireFunction<MultiTextInputQuestion, string[]>
showError: ShowFunction
}

@ -2,8 +2,8 @@ import {isPopulatedArray} from "../../utils/Arrays";
import chalk from "chalk";
import pluralize from "pluralize";
import {totalItems} from "../../datatypes/EmpathyGroupList";
import {Separator} from "inquirer";
import {editYaml} from "../../schemata/YAMLPrompt";
import {ExpandQuestion, Separator} from "inquirer";
import {editYaml, EditYamlInquire} from "../../schemata/YAMLPrompt";
import {InquireFunction, ShowFunction} from "../Inquire";
import {EmpathyGroupListPromptOptions} from "./EmpathyGroupListPrompt";
import {EmpathyGroup} from "../../datatypes/EmpathyGroup";
@ -14,7 +14,7 @@ export interface EmpathyGuidePromptOptions {
}
export interface EmpathyGuidePromptDependencies {
readonly inquire: InquireFunction
readonly inquire: InquireFunction<ExpandQuestion, typeof PLEASANT | typeof UNPLEASANT | typeof NEEDS | typeof INSPECT | typeof SAVE> & EditYamlInquire
readonly showError: ShowFunction
readonly promptForEmpathyGroupList: (opts: EmpathyGroupListPromptOptions) => Promise<readonly EmpathyGroup[]>
}

@ -1,13 +1,15 @@
import {InquireFunction, ShowFunction} from "../Inquire";
import {ExpandChoiceOptions, Separator, SeparatorOptions} from "inquirer";
import {ExpandChoiceOptions, ExpandQuestion, Separator, SeparatorOptions} from "inquirer";
import {merge} from "../../utils/Merge";
import {Entry, EntryJTD} from "../../datatypes/Entry";
import {editYaml} from "../../schemata/YAMLPrompt";
import {editYaml, EditYamlInquire} from "../../schemata/YAMLPrompt";
const VIEW = ".View"
const DONE = ".Done"
export interface EntryMainMenuChoice<PropertyT extends string & keyof EntryMainMenuOptions> {
type EntryMainMenuChoiceKey = string & keyof EntryMainMenuOptions
export interface EntryMainMenuChoice<PropertyT extends EntryMainMenuChoiceKey> {
readonly type: "mainMenu"
readonly property: PropertyT
choice(input: EntryMainMenuOptions[PropertyT]): ExpandChoiceOptions & { value: PropertyT }
@ -57,8 +59,8 @@ export function makeEntryMainMenuChoice<PropertyT extends string & keyof EntryMa
export interface EntryMainMenuOptions extends Omit<Entry, "finishedAt"> {}
export interface EntryMainMenuDependencies {
readonly inquire: InquireFunction
readonly choices: readonly (EntryMainMenuChoice<any>|SeparatorOptions)[]
readonly inquire: InquireFunction<ExpandQuestion, typeof DONE | typeof VIEW | EntryMainMenuChoiceKey> & EditYamlInquire
readonly choices: readonly (EntryMainMenuChoice<EntryMainMenuChoiceKey>|SeparatorOptions)[]
readonly showError: ShowFunction
}
@ -106,7 +108,7 @@ export async function promptForEntryMainMenu(entry: EntryMainMenuOptions, deps:
const withoutFinishedAt = {...updated, finishedAt: undefined}
return continueWith(withoutFinishedAt)
} else {
const selectedChoice = choices.find((choice) => choice.type === "mainMenu" && choice.property === result) as EntryMainMenuChoice<any>
const selectedChoice = choices.find((choice) => choice.type === "mainMenu" && choice.property === result) as EntryMainMenuChoice<string & keyof EntryMainMenuOptions>
return continueWith(await onSelected(selectedChoice, entry))
}
}

@ -5,7 +5,7 @@ import {EntryMainMenuChoice, makeEntryMainMenuChoice} from "./EntryMainMenuPromp
import chalk from "chalk";
import pluralize from "pluralize";
import {isPopulatedArray} from "../../utils/Arrays";
import {ListChoiceOptions} from "inquirer";
import {ListChoiceOptions, ListQuestion} from "inquirer";
import {asDefault} from "../../utils/Objects";
import {GuidedEmpathyPromptOptions} from "./GuidedEmpathyPrompt";
@ -14,7 +14,7 @@ export interface GuidedEmpathyListPromptOptions extends PersonaPrompt {
}
export interface GuidedEmpathyListPromptDependencies {
readonly inquire: InquireFunction
readonly inquire: InquireFunction<ListQuestion, number>
readonly promptForEmpathy: (input: GuidedEmpathyPromptOptions) => Promise<GuidedEmpathy|null>
}

@ -11,14 +11,14 @@ import {isPopulatedArray} from "../../utils/Arrays";
import Separator from "inquirer/lib/objects/separator";
import {EmpathyGroup} from "../../datatypes/EmpathyGroup";
import {GuidedEmpathy, guidedEmpathyToString, isPopulatedGuidedEmpathy} from "../../datatypes/GuidedEmpathy";
import {HierarchicalCheckboxChildChoice, HierarchicalCheckboxParentChoice } from "inquirer";
import {HierarchicalCheckboxChildChoice, HierarchicalCheckboxParentChoice, HierarchicalCheckboxQuestion, MultiTextInputQuestion } from "inquirer";
export interface GuidedEmpathyPromptOptions extends PersonaPrompt {
readonly default?: GuidedEmpathy
}
export interface GuidedEmpathyPromptDependencies {
readonly inquire: InquireFunction,
readonly inquire: InquireFunction<HierarchicalCheckboxQuestion, readonly string[]> & InquireFunction<MultiTextInputQuestion, readonly string[]>
readonly guideFactory: () => EmpathyGuide,
readonly show: ShowFunction,
}

@ -10,7 +10,7 @@ import {asDefault} from "../../utils/Objects";
export interface JournalEntryPromptOptions extends Partial<Omit<EditorQuestion, "name" | "type">>, PersonaPrompt {}
export interface JournalEntryPromptDependencies {
readonly inquire: InquireFunction
readonly inquire: InquireFunction<EditorQuestion, string>
}
export function journalEntryPrompt(deps: JournalEntryPromptDependencies): {

@ -9,7 +9,7 @@ export interface SuicidalityPromptOptions extends Partial<Omit<ListQuestion, "na
}
export interface SuicidalityPromptDependencies {
inquire: InquireFunction
inquire: InquireFunction<ListQuestion, Suicidality>
}
export function suicidalityPrompt(deps: SuicidalityPromptDependencies): {

@ -10,7 +10,7 @@ import {asDefault} from "../../utils/Objects";
export interface SummaryPromptOptions extends Partial<Omit<InputQuestion, "name" | "type">>, PersonaPrompt {}
export interface SummaryPromptDependencies {
readonly inquire: InquireFunction
readonly inquire: InquireFunction<InputQuestion, string>
}
export function summaryPrompt(deps: SummaryPromptDependencies): {

@ -17,6 +17,7 @@ import figures from "figures";
import Paginator from "inquirer/lib/utils/paginator";
import {isPopulatedArray} from "../../utils/Arrays";
import {filter as fuzzyFilter, FilterOptions} from "fuzzy";
import ScreenManager from "inquirer/lib/utils/screen-manager";
interface ExtendedReadLine extends ReadLineInterface {
line: string
@ -51,7 +52,7 @@ declare module "inquirer" {
export interface HierarchicalCheckboxQuestion<AnswerT extends Answers = Answers> extends Question<AnswerT>, HierarchicalCheckboxQuestionOptions {
/** @inheritDoc */
type: "hierarchical-checkbox"
default?: string[]
default?: readonly string[]
}
export interface QuestionMap {
@ -59,6 +60,14 @@ declare module "inquirer" {
}
}
interface ExtendedPaginatorConstructor {
new(manager: ScreenManager, options: {isInfinite:boolean}): ExtendedPaginator
}
interface ExtendedPaginator extends Paginator {
paginate(content: string, selectedIndex: number, pageSize?: number): string;
}
interface NormalizedChoiceBase {
readonly name: string
readonly value: string|null
@ -260,14 +269,17 @@ export class HierarchicalCheckboxInput<AnswerT extends Answers = Answers, Questi
this.updateUnlistedValues()
}
// @ts-ignore
_run(callback: HierarchicalCheckboxInput["done"], error: (err: Error) => void) {
_run(callback: Exclude<HierarchicalCheckboxInput["done"], null>, error?: (err: Error) => void) {
const invalidChoices = Object.keys(this.invalidValues);
const invalidValues = this.value.filter((value) => !this.valueMap.hasOwnProperty(value));
if (isPopulatedArray(invalidChoices)) {
error(Error(`Duplicate values: ${invalidChoices.join()}`))
if (error) {
error(Error(`Duplicate values: ${invalidChoices.join()}`))
}
} else if (isPopulatedArray(invalidValues)) {
error(Error(`Unknown values: ${invalidValues.join()}`))
if (error) {
error(Error(`Unknown values: ${invalidValues.join()}`))
}
}
this.rl.on("history", (history) => {
history.splice(0, history.length)
@ -294,13 +306,12 @@ export class HierarchicalCheckboxInput<AnswerT extends Answers = Answers, Questi
private readonly valueMap: ValidValueMap
private readonly invalidValues: InvalidValueMap
private readonly value: string[]
private done: (state: any) => void|null
private done: ((state: unknown) => void)|null
private location: Breadcrumb
private activeIndex: number
private unlistedValues: string[]
private submitter: Subject<string[]> = new Subject<string[]>()
// @ts-ignore
private paginator: Paginator = new Paginator(this.screen, {isInfinite: true});
private paginator: ExtendedPaginator = new (Paginator as ExtendedPaginatorConstructor)(this.screen, {isInfinite: true});
private scheduledRender: NodeJS.Immediate|null = null
private get readline(): ExtendedReadLine {
@ -563,7 +574,7 @@ export class HierarchicalCheckboxInput<AnswerT extends Answers = Answers, Questi
children: this.readline.line === "" ? this.location.searchable : fuzzyFilter(this.readline.line, this.location.searchable.slice(), {
pre: searchHighlightPrefix,
post: searchHighlightSuffix,
extract(input: any): string {
extract(input: NormalizedLeafChoice | NormalizedParentChoice): string {
return input.name
}
} as FilterOptions<NormalizedParentChoice | NormalizedLeafChoice>).map((result) => ({
@ -644,7 +655,9 @@ export class HierarchicalCheckboxInput<AnswerT extends Answers = Answers, Questi
this.status = "answered"
this.render()
this.screen.done()
this.done(value)
if (this.done !== null) {
this.done(value)
}
}
private onOtherKey() {

@ -21,11 +21,11 @@ declare module "inquirer" {
export interface MultiTextInputQuestion<AnswerT extends Answers = Answers> extends Question<AnswerT>, MultiTextInputQuestionOptions {
/** @inheritDoc */
type: "multitext"
default?: string[]
default?: readonly string[]
}
export interface QuestionMap {
["multitext"]: HierarchicalCheckboxQuestion
["multitext"]: MultiTextInputQuestion
}
}
@ -60,7 +60,7 @@ export class MultiTextInput<AnswerT extends Answers = Answers, QuestionT extends
this.scheduleRender()
}
private done: (state: any) => void|null
private done: (state: unknown) => void|null
private activeIndex: number
private readonly value: string[]
private submitter: Subject<string[]> = new Subject<string[]>()

@ -5,7 +5,7 @@ import {
ReferencedTypes,
schema,
SchemaData,
UntypedReferenceList
AnyReferenceList, Value
} from "../schemata/SchemaData";
import {rm, open, FileHandle} from "fs/promises";
import {join, dirname} from "path";
@ -25,6 +25,12 @@ export const AutosaveFileJTD = schema({
references: []
})
export interface LocalRepositoryDependencies {
rm: typeof rm
open: typeof open
makeDir: typeof makeDir
}
export class LocalRepository {
private readonly paths: envPaths.Paths
@ -120,10 +126,11 @@ export class LocalRepository {
}
}
async loadAutosaveObjects<TypesT extends UntypedReferenceList>(
// TODO: Enable autosave for all commands
async loadAutosaveObjects<TypesT extends AnyReferenceList>(
{autosaveNamespace, schemas}: { autosaveNamespace: string, schemas: TypesT }): Promise<{
validated: Partial<ReferencedTypes<TypesT>>,
unvalidated: Partial<Record<keyof ReferencedTypes<TypesT>, unknown>>
unvalidated: Partial<Record<keyof ReferencedTypes<TypesT>, string>>
}> {
const path = this.getAutosaveFilePath(autosaveNamespace)
let handle: FileHandle
@ -143,14 +150,14 @@ export class LocalRepository {
throw Error(`Autosave file ${autosaveNamespace} was too corrupted to make sense of`)
}
const validated: Partial<ReferencedTypes<TypesT>> = {}
const unvalidated: Partial<Record<keyof ReferencedTypes<TypesT>, unknown>> = {}
const unvalidated: Partial<Record<keyof ReferencedTypes<TypesT>, string>> = {}
schemas.forEach((schema: ReferencedSchema<TypesT>) => {
if (autosaved.hasOwnProperty(schema.key)) {
const value = autosaved[schema.key]
if (!schema.validate(value)) {
unvalidated[schema.key as keyof ReferencedTypes<TypesT>] = value
} else {
validated[schema.key as keyof ReferencedTypes<TypesT>] = value
validated[schema.key as keyof ReferencedTypes<TypesT>] = value as Value<ReferencedSchema<TypesT>>
}
}
})
@ -160,7 +167,7 @@ export class LocalRepository {
}
}
async saveAutosaveObject<T>({schema, value, autosaveNamespace}: {autosaveNamespace: string, schema: SchemaData<T, any, any, any>, value: T}): Promise<void> {
async saveAutosaveObject<T>({schema, value, autosaveNamespace}: {autosaveNamespace: string, schema: SchemaData<T, string, AnyReferenceList, ReferencedTypes<AnyReferenceList>>, value: T}): Promise<void> {
const path = this.getAutosaveFilePath(autosaveNamespace)
await makeDir(dirname(path))
const handle = await open(path, "a+")
@ -178,7 +185,7 @@ export class LocalRepository {
}
}
async clearAutosaveObject({schema, autosaveNamespace}: {autosaveNamespace: string, schema: SchemaData<any, any, any, any>}): Promise<void> {
async clearAutosaveObject({schema, autosaveNamespace}: {autosaveNamespace: string, schema: SchemaData<unknown, string, AnyReferenceList, ReferencedTypes<AnyReferenceList>>}): Promise<void> {
const path = this.getAutosaveFilePath(autosaveNamespace)
await makeDir(dirname(path))
const handle = await open(path, "a+")

@ -3,7 +3,18 @@ import AJV, {ValidateFunction, JTDSchemaType} from "ajv/dist/jtd";
const ajv = new AJV();
export type SchemaData<RepresentedT, ReferenceT extends string, ReferencesT extends UntypedReferenceList, DefinitionsT extends ReferencedTypes<ReferencesT>> = {
interface BaseSchemaData<RepresentedT, ReferenceT extends string> {
value?: RepresentedT,
schema: unknown,
key: ReferenceT,
definition: { [key in ReferenceT]: unknown }
referenced?: { [key in ReferenceT]: RepresentedT }
reference: { "ref": ReferenceT },
validate: ValidateFunction<RepresentedT>
requiredReferences: AnyReferenceList
}
export interface SchemaData<RepresentedT, ReferenceT extends string, ReferencesT extends AnyReferenceList, DefinitionsT extends ReferencedTypes<ReferencesT>> extends BaseSchemaData<RepresentedT, ReferenceT> {
value?: RepresentedT,
schema: JTDSchemaType<RepresentedT, DefinitionsT>,
key: ReferenceT,
@ -14,35 +25,38 @@ export type SchemaData<RepresentedT, ReferenceT extends string, ReferencesT exte
requiredReferences: ReferencesT
}
export type UntypedReferenceList = SchemaData<any, any, any, any>[]
export type AnySchemaData = BaseSchemaData<unknown, string>
export type AnySchemaDataFor<RepresentedT> = BaseSchemaData<RepresentedT, string>
export type AnyReferenceList = AnySchemaData[]
export type AnyDefinitions = Record<string, unknown>
export type Schema<DataT extends SchemaData<any, any, any, any>> = Exclude<DataT["schema"], undefined>
export type Value<DataT extends SchemaData<any, any, any, any>> = Exclude<DataT["value"], undefined>
export type Definition<DataT extends SchemaData<any, any, any, any>> = Exclude<DataT["definition"], undefined>
export type Reference<DataT extends SchemaData<any, any, any, any>> = Exclude<DataT["reference"], undefined>
export type ReferenceKey<DataT extends SchemaData<any, any, any, any>> = Exclude<DataT["key"], undefined>
export type Referenced<DataT extends SchemaData<any, any, any, any>> = Exclude<DataT["referenced"], undefined>
export type Schema<DataT extends AnySchemaData> = Exclude<DataT["schema"], undefined>
export type Value<DataT extends AnySchemaData> = Exclude<DataT["value"], undefined>
export type Definition<DataT extends AnySchemaData> = Exclude<DataT["definition"], undefined>
export type Reference<DataT extends AnySchemaData> = Exclude<DataT["reference"], undefined>
export type ReferenceKey<DataT extends AnySchemaData> = Exclude<DataT["key"], undefined>
export type Referenced<DataT extends AnySchemaData> = Exclude<DataT["referenced"], undefined>
export type ReferencedSchemaMap<ReferencesT extends UntypedReferenceList> = {
[Property in keyof ReferencesT as ReferencesT[Property] extends SchemaData<any, any, any, any> ? ReferenceKey<ReferencesT[Property]> : never]: ReferencesT[Property] extends SchemaData<any, any, any, any> ? ReferencesT[Property] : never
export type ReferencedSchemaMap<ReferencesT extends AnyReferenceList> = {
[Property in keyof ReferencesT as ReferencesT[Property] extends AnySchemaData ? ReferenceKey<ReferencesT[Property]> : never]: ReferencesT[Property] extends AnySchemaData ? ReferencesT[Property] : never
}
export type ReferencedSchema<ReferencesT extends UntypedReferenceList> = ReferencedSchemaMap<ReferencesT>[keyof ReferencedSchemaMap<ReferencesT>]
export type ReferencedDefinitions<ReferencesT extends UntypedReferenceList> = {
export type ReferencedSchema<ReferencesT extends AnyReferenceList> = ReferencedSchemaMap<ReferencesT>[keyof ReferencedSchemaMap<ReferencesT>]
export type ReferencedDefinitions<ReferencesT extends AnyReferenceList> = {
[Property in keyof ReferencedSchemaMap<ReferencesT>]?: Definition<ReferencedSchemaMap<ReferencesT>[Property]>
}
export type ReferencedTypes<ReferencesT extends UntypedReferenceList> = {
export type ReferencedTypes<ReferencesT extends AnyReferenceList> = {
[Property in keyof ReferencedSchemaMap<ReferencesT>]?: Value<ReferencedSchemaMap<ReferencesT>[Property]>
}
export function schema<
RepresentedT,
KeyT extends string,
ReferencesT extends SchemaData<any, any, any, ReferencedTypes<ReferencesT>>[],
ReferencesT extends AnySchemaData[],
>({schema, key, references}: { schema: JTDSchemaType<RepresentedT, ReferencedTypes<ReferencesT>>, key: KeyT, references: ReferencesT, typeHint?: RepresentedT|null }): SchemaData<RepresentedT, KeyT, ReferencesT, ReferencedTypes<ReferencesT>> {
const definition = {[key]: schema} as Definition<SchemaData<RepresentedT, KeyT, ReferencesT, ReferencedTypes<ReferencesT>>>
const reference = {ref: key}
const definitions = references.reduce((definitions, reference) => ({...reference.definition, ...definitions}), {} as ReferencedDefinitions<ReferencesT>)
const validate = ajv.compile<RepresentedT>({ ...schema, definitions })
const definitions = references.reduce<AnyDefinitions>((definitions, reference) => ({...reference.definition, ...definitions}), definition)
const validate = ajv.compile<RepresentedT>({ ...reference, definitions })
return {
schema,
key,

@ -1,14 +1,17 @@
import {SchemaData} from "./SchemaData";
import {AnySchemaDataFor} from "./SchemaData";
import {dump, load} from "js-yaml";
import {EditorQuestionOptions} from "inquirer";
import {InquireFunction, ShowFunction} from "../prompts/Inquire";
import {EditorQuestion, ExpandQuestion} from "inquirer";
const RETRY = "Retry"
const ABORT = "Abort"
export async function editYaml<ObjectT>({schema, currentValue, name, inquire, showError}: {schema: SchemaData<ObjectT, any, any, any>, currentValue: ObjectT, name: string, inquire: InquireFunction, showError: ShowFunction}): Promise<ObjectT>
export async function editYaml<ObjectT>({schema, currentValue, name, inquire, showError}: {schema: SchemaData<ObjectT, any, any, any>, currentValue: ObjectT|undefined, name: string, inquire: InquireFunction, showError: ShowFunction}): Promise<ObjectT|undefined>
export async function editYaml<ObjectT>({schema, currentValue, name, inquire, showError}: {schema: SchemaData<ObjectT, any, any, any>, currentValue: ObjectT|undefined, name: string, inquire: InquireFunction, showError: ShowFunction}): Promise<ObjectT|undefined> {
export type EditYamlInquire = InquireFunction<EditorQuestion, string> & InquireFunction<ExpandQuestion, typeof RETRY|typeof ABORT>
// TODO: Inject this one's dependencies (inquire, showError) as well, instead of making callers do it, and give it to said callers.
export async function editYaml<ObjectT extends object>({schema, currentValue, name, inquire, showError}: {schema: AnySchemaDataFor<ObjectT>, currentValue: ObjectT, name: string, inquire: EditYamlInquire, showError: ShowFunction}): Promise<ObjectT>
export async function editYaml<ObjectT extends object>({schema, currentValue, name, inquire, showError}: {schema: AnySchemaDataFor<ObjectT>, currentValue: ObjectT|undefined, name: string, inquire: EditYamlInquire, showError: ShowFunction}): Promise<ObjectT|undefined>
export async function editYaml<ObjectT extends object>({schema, currentValue, name, inquire, showError}: {schema: AnySchemaDataFor<ObjectT>, currentValue: ObjectT|undefined, name: string, inquire: EditYamlInquire, showError: ShowFunction}): Promise<ObjectT|undefined> {
const original = dump(currentValue)
let text = original
while (true) {
@ -16,7 +19,7 @@ export async function editYaml<ObjectT>({schema, currentValue, name, inquire, sh
type: "editor",
message: `View and edit ${name}:`,
default: text,
} as EditorQuestionOptions)
})
if (original !== modified) {
try {
const result = load(modified)

@ -1,4 +1,4 @@
export function merge<Update extends {readonly [key: string]: any}, Data extends Update>(update: Update, base: Data): Data {
export function merge<Update extends {readonly [key: string]: unknown}, Data extends Update>(update: Update, base: Data): Data {
const data = {
...update,
...base,

Loading…
Cancel
Save