@open-discord-bots/framework 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/LICENSE.md +713 -0
  2. package/README.md +104 -0
  3. package/dist/api/api.d.ts +26 -0
  4. package/dist/api/api.js +44 -0
  5. package/dist/api/main.d.ts +133 -0
  6. package/dist/api/main.js +87 -0
  7. package/dist/api/modules/action.d.ts +34 -0
  8. package/dist/api/modules/action.js +58 -0
  9. package/dist/api/modules/base.d.ts +329 -0
  10. package/dist/api/modules/base.js +804 -0
  11. package/dist/api/modules/builder.d.ts +647 -0
  12. package/dist/api/modules/builder.js +1441 -0
  13. package/dist/api/modules/checker.d.ts +648 -0
  14. package/dist/api/modules/checker.js +1324 -0
  15. package/dist/api/modules/client.d.ts +768 -0
  16. package/dist/api/modules/client.js +1859 -0
  17. package/dist/api/modules/code.d.ts +33 -0
  18. package/dist/api/modules/code.js +57 -0
  19. package/dist/api/modules/config.d.ts +70 -0
  20. package/dist/api/modules/config.js +206 -0
  21. package/dist/api/modules/console.d.ts +305 -0
  22. package/dist/api/modules/console.js +598 -0
  23. package/dist/api/modules/cooldown.d.ts +138 -0
  24. package/dist/api/modules/cooldown.js +359 -0
  25. package/dist/api/modules/database.d.ts +135 -0
  26. package/dist/api/modules/database.js +271 -0
  27. package/dist/api/modules/event.d.ts +43 -0
  28. package/dist/api/modules/event.js +100 -0
  29. package/dist/api/modules/flag.d.ts +40 -0
  30. package/dist/api/modules/flag.js +72 -0
  31. package/dist/api/modules/fuse.d.ts +218 -0
  32. package/dist/api/modules/fuse.js +123 -0
  33. package/dist/api/modules/helpmenu.d.ts +106 -0
  34. package/dist/api/modules/helpmenu.js +167 -0
  35. package/dist/api/modules/language.d.ts +85 -0
  36. package/dist/api/modules/language.js +195 -0
  37. package/dist/api/modules/permission.d.ts +121 -0
  38. package/dist/api/modules/permission.js +314 -0
  39. package/dist/api/modules/plugin.d.ts +128 -0
  40. package/dist/api/modules/plugin.js +168 -0
  41. package/dist/api/modules/post.d.ts +44 -0
  42. package/dist/api/modules/post.js +92 -0
  43. package/dist/api/modules/progressbar.d.ts +108 -0
  44. package/dist/api/modules/progressbar.js +233 -0
  45. package/dist/api/modules/responder.d.ts +506 -0
  46. package/dist/api/modules/responder.js +1468 -0
  47. package/dist/api/modules/session.d.ts +58 -0
  48. package/dist/api/modules/session.js +171 -0
  49. package/dist/api/modules/startscreen.d.ts +165 -0
  50. package/dist/api/modules/startscreen.js +293 -0
  51. package/dist/api/modules/stat.d.ts +142 -0
  52. package/dist/api/modules/stat.js +293 -0
  53. package/dist/api/modules/verifybar.d.ts +54 -0
  54. package/dist/api/modules/verifybar.js +60 -0
  55. package/dist/api/modules/worker.d.ts +41 -0
  56. package/dist/api/modules/worker.js +93 -0
  57. package/dist/api/utils.d.ts +61 -0
  58. package/dist/api/utils.js +254 -0
  59. package/dist/index.d.ts +4 -1
  60. package/dist/index.js +40 -0
  61. package/dist/startup/dump.d.ts +14 -0
  62. package/dist/startup/dump.js +79 -0
  63. package/dist/startup/errorHandling.d.ts +2 -0
  64. package/dist/startup/errorHandling.js +43 -0
  65. package/dist/startup/pluginLauncher.d.ts +2 -0
  66. package/dist/startup/pluginLauncher.js +202 -0
  67. package/package.json +9 -3
  68. package/src/api/api.ts +29 -0
  69. package/src/api/main.ts +189 -0
  70. package/src/api/modules/action.ts +58 -0
  71. package/src/api/modules/base.ts +811 -0
  72. package/src/api/modules/builder.ts +1554 -0
  73. package/src/api/modules/checker.ts +1549 -0
  74. package/src/api/modules/client.ts +2247 -0
  75. package/src/api/modules/code.ts +58 -0
  76. package/src/api/modules/config.ts +159 -0
  77. package/src/api/modules/console.ts +665 -0
  78. package/src/api/modules/cooldown.ts +348 -0
  79. package/src/api/modules/database.ts +278 -0
  80. package/src/api/modules/event.ts +99 -0
  81. package/src/api/modules/flag.ts +73 -0
  82. package/src/api/modules/fuse.ts +348 -0
  83. package/src/api/modules/helpmenu.ts +216 -0
  84. package/src/api/modules/language.ts +201 -0
  85. package/src/api/modules/permission.ts +340 -0
  86. package/src/api/modules/plugin.ts +242 -0
  87. package/src/api/modules/post.ts +90 -0
  88. package/src/api/modules/progressbar.ts +232 -0
  89. package/src/api/modules/responder.ts +1420 -0
  90. package/src/api/modules/session.ts +155 -0
  91. package/src/api/modules/startscreen.ts +320 -0
  92. package/src/api/modules/stat.ts +313 -0
  93. package/src/api/modules/verifybar.ts +61 -0
  94. package/src/api/modules/worker.ts +93 -0
  95. package/src/api/utils.ts +206 -0
  96. package/src/cli/cli.ts +151 -0
  97. package/src/cli/editConfig.ts +943 -0
  98. package/src/index.ts +6 -1
  99. package/src/startup/compilation.ts +186 -0
  100. package/src/startup/dump.ts +45 -0
  101. package/src/startup/errorHandling.ts +38 -0
  102. package/src/startup/pluginLauncher.ts +261 -0
  103. package/LICENSE +0 -21
@@ -0,0 +1,943 @@
1
+ import {api, utilities} from "../index"
2
+ import {Terminal, terminal} from "terminal-kit"
3
+ import ansis from "ansis"
4
+ import {ODCliHeaderOpts, renderHeader} from "./cli"
5
+
6
+ export class ODCliEditConfigInstance {
7
+ private readonly opts: ODCliHeaderOpts
8
+ private readonly opendiscord: api.ODMain
9
+
10
+ constructor(opts:ODCliHeaderOpts,opendiscord:api.ODMain){
11
+ this.opts = opts
12
+ this.opendiscord = opendiscord
13
+ }
14
+
15
+ public async renderEditConfig(backFn:(() => api.ODPromiseVoid)){
16
+ renderHeader(this.opts,[])
17
+ terminal(ansis.bold.green("Please select which config you would like to edit.\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
18
+
19
+ const checkerList = this.opendiscord.checkers.getAll()
20
+ const checkerNameList = checkerList.map((checker) => (checker.options.cliDisplayName ? checker.options.cliDisplayName+" ("+checker.config.file+")" : checker.config.file))
21
+ const checkerNameLength = utilities.getLongestLength(checkerNameList)
22
+ const finalCheckerNameList = checkerNameList.map((name,index) => name.padEnd(checkerNameLength+5," ")+ansis.gray(checkerList[index].options.cliDisplayDescription ? "=> "+checkerList[index].options.cliDisplayDescription : ""))
23
+
24
+ const answer = await terminal.singleColumnMenu(finalCheckerNameList,{
25
+ leftPadding:"> ",
26
+ style:terminal.cyan,
27
+ selectedStyle:terminal.bgDefaultColor.bold,
28
+ submittedStyle:terminal.bgBlue,
29
+ extraLines:2,
30
+ cancelable:true
31
+ }).promise
32
+
33
+ if (answer.canceled) return await backFn()
34
+ const checker = checkerList[answer.selectedIndex]
35
+ const configData = checker.config.data as api.ODValidJsonType
36
+ await this.chooseConfigStructure(checker,async () => {await this.renderEditConfig(backFn)},checker.structure,configData,{},NaN,["("+checker.config.path+")"])
37
+ }
38
+
39
+ private async chooseConfigStructure(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),structure:api.ODCheckerStructure,data:api.ODValidJsonType,parent:object|any[],parentIndex:string|number,path:(string|number)[]){
40
+ if (structure instanceof api.ODCheckerObjectStructure && typeof data == "object" && !Array.isArray(data) && data) await this.renderConfigObjectStructureSelector(checker,backFn,structure,data,parent,parentIndex,path)
41
+ else if (structure instanceof api.ODCheckerEnabledObjectStructure && typeof data == "object" && !Array.isArray(data) && data) await this.renderConfigEnabledObjectStructureSelector(checker,backFn,structure,data,parent,parentIndex,path)
42
+ else if (structure instanceof api.ODCheckerObjectSwitchStructure && typeof data == "object" && !Array.isArray(data) && data) await this.renderConfigObjectSwitchStructureSelector(checker,backFn,structure,data,parent,parentIndex,path)
43
+ else if (structure instanceof api.ODCheckerArrayStructure && Array.isArray(data)) await this.renderConfigArrayStructureSelector(checker,backFn,structure,data,parent,parentIndex,path)
44
+ else if (structure instanceof api.ODCheckerBooleanStructure && typeof data == "boolean") await this.renderConfigBooleanStructureEditor(checker,backFn,structure,data,parent,parentIndex,path)
45
+ else if (structure instanceof api.ODCheckerNumberStructure && typeof data == "number") await this.renderConfigNumberStructureEditor(checker,backFn,structure,data,parent,parentIndex,path)
46
+ else if (structure instanceof api.ODCheckerStringStructure && typeof data == "string") await this.renderConfigStringStructureEditor(checker,backFn,structure,data,parent,parentIndex,path)
47
+ else if (structure instanceof api.ODCheckerNullStructure && data === null) await this.renderConfigNullStructureEditor(checker,backFn,structure,data,parent,parentIndex,path)
48
+ else if (structure instanceof api.ODCheckerTypeSwitchStructure) await this.renderConfigTypeSwitchStructureEditor(checker,backFn,structure,data,parent,parentIndex,path)
49
+ else terminal.red.bold("❌ Unable to detect type of variable! Please try to edit this property in the JSON file itself!")
50
+ }
51
+
52
+ private async renderConfigObjectStructureSelector(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),structure:api.ODCheckerObjectStructure,data:object,parent:object,parentIndex:string|number,path:(string|number)[]){
53
+ if (typeof data != "object" || Array.isArray(data)) throw new api.ODSystemError("OD CLI => Property is not of the type 'object'. Please check your config for possible errors. (index: "+parentIndex+", path: "+path.join(".")+")")
54
+ renderHeader(this.opts,path)
55
+ terminal(ansis.bold.green("Please select which variable you would like to edit.\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
56
+ if (!structure.options.children) return await backFn()
57
+
58
+ if (structure.options.cliDisplayName){
59
+ terminal.gray("\nProperty: "+ansis.bold.blue(structure.options.cliDisplayName)+"\n")
60
+ terminal.gray("Description: "+ansis.bold(structure.options.cliDisplayDescription ?? "/")+"\n")
61
+ }
62
+
63
+ const list = structure.options.children.filter((child) => !child.cliHideInEditMode)
64
+ const nameList = list.map((child) => (child.checker.options.cliDisplayName ? child.checker.options.cliDisplayName : child.key))
65
+ const nameLength = utilities.getLongestLength(nameList)
66
+ const finalnameList = nameList.map((name,index) => name.padEnd(nameLength+5," ")+ansis.gray((!list[index].checker.options.cliHideDescriptionInParent && list[index].checker.options.cliDisplayDescription) ? "=> "+list[index].checker.options.cliDisplayDescription : ""))
67
+
68
+ const answer = await terminal.singleColumnMenu(finalnameList,{
69
+ leftPadding:"> ",
70
+ style:terminal.cyan,
71
+ selectedStyle:terminal.bgDefaultColor.bold.defaultColor,
72
+ submittedStyle:terminal.bgBlue,
73
+ extraLines:2,
74
+ cancelable:true
75
+ }).promise
76
+
77
+ if (answer.canceled) return await backFn()
78
+ const subStructure = list[answer.selectedIndex]
79
+ const subData = data[subStructure.key]
80
+ await this.chooseConfigStructure(checker,async () => {await this.renderConfigObjectStructureSelector(checker,backFn,structure,data,parent,parentIndex,path)},subStructure.checker,subData,data,subStructure.key,[...path,subStructure.key])
81
+ }
82
+
83
+ private async renderConfigEnabledObjectStructureSelector(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),structure:api.ODCheckerEnabledObjectStructure,data:object,parent:object,parentIndex:string|number,path:(string|number)[]){
84
+ if (typeof data != "object" || Array.isArray(data)) throw new api.ODSystemError("OD CLI => Property is not of the type 'object'. Please check your config for possible errors. (index: "+parentIndex+", path: "+path.join(".")+")")
85
+ const enabledProperty = structure.options.property
86
+ const subStructure = structure.options.checker
87
+ if (!enabledProperty || !subStructure || !subStructure.options.children) return await backFn()
88
+
89
+ if (!subStructure.options.children.find((child) => child.key === structure.options.property)){
90
+ if (typeof structure.options.enabledValue == "string") subStructure.options.children.unshift({key:enabledProperty,optional:false,priority:1,checker:new api.ODCheckerStringStructure("opendiscord:CLI-checker-enabled-object-structure",{})})
91
+ else if (typeof structure.options.enabledValue == "number") subStructure.options.children.unshift({key:enabledProperty,optional:false,priority:1,checker:new api.ODCheckerNumberStructure("opendiscord:CLI-checker-enabled-object-structure",{})})
92
+ else if (typeof structure.options.enabledValue == "boolean") subStructure.options.children.unshift({key:enabledProperty,optional:false,priority:1,checker:new api.ODCheckerBooleanStructure("opendiscord:CLI-checker-enabled-object-structure",{})})
93
+ }
94
+
95
+ await this.chooseConfigStructure(checker,backFn,subStructure,data,parent,parentIndex,path)
96
+ }
97
+
98
+ private async renderConfigObjectSwitchStructureSelector(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),structure:api.ODCheckerObjectSwitchStructure,data:object,parent:object,parentIndex:string|number,path:(string|number)[]){
99
+ if (typeof data != "object" || Array.isArray(data)) throw new api.ODSystemError("OD CLI => Property is not of the type 'object'. Please check your config for possible errors. (index: "+parentIndex+", path: "+path.join(".")+")")
100
+ if (!structure.options.objects) return await backFn()
101
+
102
+ let didMatch: boolean = false
103
+ for (const objectTemplate of structure.options.objects){
104
+ if (objectTemplate.properties.every((prop) => data[prop.key] === prop.value)){
105
+ //object template matches data
106
+ const subStructure = objectTemplate.checker
107
+ didMatch = true
108
+ await this.chooseConfigStructure(checker,backFn,subStructure,data,parent,parentIndex,path)
109
+ }
110
+ }
111
+ if (!didMatch) throw new api.ODSystemError("OD CLI => Unable to detect type of object in the object switch. Please check your config for possible errors. (index: "+parentIndex+", path: "+path.join(".")+")")
112
+ }
113
+
114
+ private async renderConfigArrayStructureSelector(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),structure:api.ODCheckerArrayStructure,data:any[],parent:object,parentIndex:string|number,path:(string|number)[]){
115
+ if (!Array.isArray(data)) throw new api.ODSystemError("OD CLI => Property is not of the type 'array'. Please check your config for possible errors. (index: "+parentIndex+", path: "+path.join(".")+")")
116
+ renderHeader(this.opts,path)
117
+ terminal(ansis.bold.green("Please select what you would like to do.\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
118
+ if (!structure.options.propertyChecker) return await backFn()
119
+
120
+ if (structure.options.cliDisplayName || typeof parentIndex == "string" || !isNaN(parentIndex)){
121
+ terminal.gray("\nProperty: "+ansis.bold.blue(structure.options.cliDisplayName ?? parentIndex.toString())+"\n")
122
+ terminal.gray("Description: "+ansis.bold(structure.options.cliDisplayDescription ?? "/")+"\n")
123
+ }
124
+
125
+ const propertyName = structure.options.cliDisplayPropertyName ?? "index"
126
+ const answer = await terminal.singleColumnMenu(data.length < 1 ? ["Add "+propertyName] : [
127
+ "Add "+propertyName,
128
+ "Edit "+propertyName,
129
+ "Move "+propertyName,
130
+ "Remove "+propertyName,
131
+ "Duplicate "+propertyName
132
+ ],{
133
+ leftPadding:"> ",
134
+ style:terminal.cyan,
135
+ selectedStyle:terminal.bgDefaultColor.bold,
136
+ submittedStyle:terminal.bgBlue,
137
+ extraLines:2,
138
+ cancelable:true
139
+ }).promise
140
+
141
+ const backFnFunc = async () => {await this.renderConfigArrayStructureSelector(checker,backFn,structure,data,parent,parentIndex,path)}
142
+
143
+ if (answer.canceled) return await backFn()
144
+ if (answer.selectedIndex == 0) await this.chooseAdditionConfigStructure(checker,backFnFunc,async (newData) => {
145
+ data[data.length] = newData
146
+ await checker.config.save()
147
+ await backFnFunc()
148
+ },structure.options.propertyChecker,data,data.length,path,[])
149
+ else if (answer.selectedIndex == 1) await this.renderConfigArrayStructureEditSelector(checker,backFnFunc,structure,structure.options.propertyChecker,data,parent,parentIndex,path)
150
+ else if (answer.selectedIndex == 2) await this.renderConfigArrayStructureMoveSelector(checker,backFnFunc,structure,structure.options.propertyChecker,data,parent,parentIndex,path)
151
+ else if (answer.selectedIndex == 3) await this.renderConfigArrayStructureRemoveSelector(checker,backFnFunc,structure,structure.options.propertyChecker,data,parent,parentIndex,path)
152
+ else if (answer.selectedIndex == 4) await this.renderConfigArrayStructureDuplicateSelector(checker,backFnFunc,structure,structure.options.propertyChecker,data,parent,parentIndex,path)
153
+ }
154
+
155
+ private async renderConfigArrayStructureEditSelector(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),arrayStructure:api.ODCheckerArrayStructure,structure:api.ODCheckerStructure,data:any[],parent:object,parentIndex:string|number,path:(string|number)[]){
156
+ const propertyName = arrayStructure.options.cliDisplayPropertyName ?? "index"
157
+ renderHeader(this.opts,path)
158
+ terminal(ansis.bold.green("Please select the "+propertyName+" you would like to edit.\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
159
+
160
+ const longestDataListName = Math.max(...data.map((d,i) => this.getArrayPreviewStructureNameLength(structure,d,data,i)))
161
+ const dataList = data.map((d,i) => (i+1)+". "+this.getArrayPreviewFromStructure(structure,d,data,i,longestDataListName))
162
+ const dataAnswer = await terminal.singleColumnMenu(dataList,{
163
+ leftPadding:"> ",
164
+ style:terminal.cyan,
165
+ selectedStyle:terminal.bgDefaultColor.bold,
166
+ submittedStyle:terminal.bgBlue,
167
+ extraLines:2,
168
+ cancelable:true
169
+ }).promise
170
+
171
+ if (dataAnswer.canceled) return await backFn()
172
+ const subData = data[dataAnswer.selectedIndex]
173
+ await this.chooseConfigStructure(checker,async () => {await this.renderConfigArrayStructureEditSelector(checker,backFn,arrayStructure,structure,data,parent,parentIndex,path)},structure,subData,data,dataAnswer.selectedIndex,[...path,dataAnswer.selectedIndex])
174
+ }
175
+
176
+ private async renderConfigArrayStructureMoveSelector(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),arrayStructure:api.ODCheckerArrayStructure,structure:api.ODCheckerStructure,data:any[],parent:object,parentIndex:string|number,path:(string|number)[]){
177
+ const propertyName = arrayStructure.options.cliDisplayPropertyName ?? "index"
178
+ renderHeader(this.opts,path)
179
+ terminal(ansis.bold.green("Please select the "+propertyName+" you would like to move.\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
180
+
181
+ const longestDataListName = Math.max(...data.map((d,i) => this.getArrayPreviewStructureNameLength(structure,d,data,i)))
182
+ const dataList = data.map((d,i) => (i+1)+". "+this.getArrayPreviewFromStructure(structure,d,data,i,longestDataListName))
183
+ const dataAnswer = await terminal.singleColumnMenu(dataList,{
184
+ leftPadding:"> ",
185
+ style:terminal.cyan,
186
+ selectedStyle:terminal.bgDefaultColor.bold,
187
+ submittedStyle:terminal.bgBlue,
188
+ extraLines:2,
189
+ cancelable:true
190
+ }).promise
191
+
192
+ if (dataAnswer.canceled) return await backFn()
193
+
194
+ renderHeader(this.opts,[...path,dataAnswer.selectedIndex])
195
+ terminal(ansis.bold.green("Please select the position you would like to move to.\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
196
+
197
+ const moveAnswer = await terminal.singleColumnMenu(data.map((d,i) => "Position "+(i+1)),{
198
+ leftPadding:"> ",
199
+ style:terminal.cyan,
200
+ selectedStyle:terminal.bgDefaultColor.bold,
201
+ submittedStyle:terminal.bgBlue,
202
+ extraLines:2,
203
+ cancelable:true
204
+ }).promise
205
+
206
+ if (moveAnswer.canceled) return await this.renderConfigArrayStructureMoveSelector(checker,backFn,arrayStructure,structure,data,parent,parentIndex,path)
207
+
208
+ const subData = data[dataAnswer.selectedIndex]
209
+ const slicedData = [...data.slice(0,dataAnswer.selectedIndex),...data.slice(dataAnswer.selectedIndex+1)]
210
+ const insertedData = [...slicedData.slice(0,moveAnswer.selectedIndex),subData,...slicedData.slice(moveAnswer.selectedIndex)]
211
+ insertedData.forEach((d,i) => data[i] = d)
212
+
213
+ await checker.config.save()
214
+ terminal.bold.blue("\n\n✅ Property moved succesfully!")
215
+ await utilities.timer(400)
216
+ await backFn()
217
+ }
218
+
219
+ private async renderConfigArrayStructureRemoveSelector(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),arrayStructure:api.ODCheckerArrayStructure,structure:api.ODCheckerStructure,data:any[],parent:object,parentIndex:string|number,path:(string|number)[]){
220
+ const propertyName = arrayStructure.options.cliDisplayPropertyName ?? "index"
221
+ renderHeader(this.opts,path)
222
+ terminal(ansis.bold.green("Please select the "+propertyName+" you would like to delete.\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
223
+
224
+ const longestDataListName = Math.max(...data.map((d,i) => this.getArrayPreviewStructureNameLength(structure,d,data,i)))
225
+ const dataList = data.map((d,i) => (i+1)+". "+this.getArrayPreviewFromStructure(structure,d,data,i,longestDataListName))
226
+ const dataAnswer = await terminal.singleColumnMenu(dataList,{
227
+ leftPadding:"> ",
228
+ style:terminal.cyan,
229
+ selectedStyle:terminal.bgDefaultColor.bold,
230
+ submittedStyle:terminal.bgBlue,
231
+ extraLines:2,
232
+ cancelable:true
233
+ }).promise
234
+
235
+ if (dataAnswer.canceled) return await backFn()
236
+ data.splice(dataAnswer.selectedIndex,1)
237
+
238
+ await checker.config.save()
239
+ terminal.bold.blue("\n\n✅ Property deleted succesfully!")
240
+ await utilities.timer(400)
241
+ await backFn()
242
+ }
243
+
244
+ private async renderConfigArrayStructureDuplicateSelector(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),arrayStructure:api.ODCheckerArrayStructure,structure:api.ODCheckerStructure,data:any[],parent:object,parentIndex:string|number,path:(string|number)[]){
245
+ const propertyName = arrayStructure.options.cliDisplayPropertyName ?? "index"
246
+ renderHeader(this.opts,path)
247
+ terminal(ansis.bold.green("Please select the "+propertyName+" you would like to duplicate.\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
248
+
249
+ const longestDataListName = Math.max(...data.map((d,i) => this.getArrayPreviewStructureNameLength(structure,d,data,i)))
250
+ const dataList = data.map((d,i) => (i+1)+". "+this.getArrayPreviewFromStructure(structure,d,data,i,longestDataListName))
251
+ const dataAnswer = await terminal.singleColumnMenu(dataList,{
252
+ leftPadding:"> ",
253
+ style:terminal.cyan,
254
+ selectedStyle:terminal.bgDefaultColor.bold,
255
+ submittedStyle:terminal.bgBlue,
256
+ extraLines:2,
257
+ cancelable:true
258
+ }).promise
259
+
260
+ if (dataAnswer.canceled) return await backFn()
261
+ data.push(JSON.parse(JSON.stringify(data[dataAnswer.selectedIndex])))
262
+
263
+ await checker.config.save()
264
+ terminal.bold.blue("\n\n✅ Property duplicated succesfully!")
265
+ await utilities.timer(400)
266
+ await backFn()
267
+ }
268
+
269
+ private async renderConfigBooleanStructureEditor(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),structure:api.ODCheckerBooleanStructure,data:boolean,parent:object,parentIndex:string|number,path:(string|number)[]){
270
+ if (typeof data != "boolean") throw new api.ODSystemError("OD CLI => Property is not of the type 'boolean'. Please check your config for possible errors. (index: "+parentIndex+", path: "+path.join(".")+")")
271
+ renderHeader(this.opts,path)
272
+ terminal(ansis.bold.green("You are now editing "+(typeof parentIndex == "string" ? "the boolean property "+ansis.blue("\""+parentIndex+"\"") : "boolean property "+ansis.blue("#"+(parentIndex+1)))+".\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
273
+
274
+ terminal.gray("\nCurrent value: "+ansis.bold[data ? "green" : "red"](data.toString())+"\n")
275
+ terminal.gray("Description: "+ansis.bold(structure.options.cliDisplayDescription ?? "/")+"\n")
276
+
277
+ const answer = await terminal.singleColumnMenu(["false (Disabled)","true (Enabled)"],{
278
+ leftPadding:"> ",
279
+ style:terminal.cyan,
280
+ selectedStyle:terminal.bgDefaultColor.bold,
281
+ submittedStyle:terminal.bgBlue,
282
+ extraLines:2,
283
+ cancelable:true
284
+ }).promise
285
+
286
+ if (answer.canceled) return await backFn()
287
+
288
+ //run config checker
289
+ const newValue = (answer.selectedIndex == 0) ? false : true
290
+ const newPath = [...path]
291
+ newPath.shift()
292
+ checker.messages = [] //manually clear previous messages
293
+ const isDataValid = structure.check(checker,newValue,newPath)
294
+
295
+ if (isDataValid){
296
+ parent[parentIndex] = newValue
297
+
298
+ await checker.config.save()
299
+ terminal.bold.blue("\n\n✅ Variable saved succesfully!")
300
+ await utilities.timer(400)
301
+ await backFn()
302
+ }else{
303
+ const messages = checker.messages.map((msg) => "=> ["+msg.type.toUpperCase()+"] "+msg.message).join("\n")
304
+ terminal.bold.blue("\n\n❌ Variable is invalid! Please try again!")
305
+ terminal.gray("\n"+messages)
306
+ await utilities.timer(1000+(2000*checker.messages.length))
307
+ await this.renderConfigBooleanStructureEditor(checker,backFn,structure,data,parent,parentIndex,path)
308
+ }
309
+ }
310
+
311
+ private async renderConfigNumberStructureEditor(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),structure:api.ODCheckerNumberStructure,data:number,parent:object,parentIndex:string|number,path:(string|number)[],prefillValue?:string){
312
+ if (typeof data != "number") throw new api.ODSystemError("OD CLI => Property is not of the type 'number'. Please check your config for possible errors. (index: "+parentIndex+", path: "+path.join(".")+")")
313
+ renderHeader(this.opts,path)
314
+ terminal(ansis.bold.green("You are now editing "+(typeof parentIndex == "string" ? "the number property "+ansis.blue("\""+parentIndex+"\"") : "number property "+ansis.blue("#"+(parentIndex+1)))+".\n")+ansis.italic.gray("(insert a new value and press enter, go back using escape)\n"))
315
+
316
+ terminal.gray("\nCurrent value: "+ansis.bold.blue(data.toString())+"\n")
317
+ terminal.gray("Description: "+ansis.bold(structure.options.cliDisplayDescription ?? "/")+"\n")
318
+
319
+ const answer = await terminal.inputField({
320
+ default:prefillValue,
321
+ style:terminal.cyan,
322
+ cancelable:true
323
+ }).promise
324
+
325
+ if (typeof answer != "string") return await backFn()
326
+
327
+ //run config checker
328
+ const newValue = Number(answer.replaceAll(",","."))
329
+ const newPath = [...path]
330
+ newPath.shift()
331
+ checker.messages = [] //manually clear previous messages
332
+ const isDataValid = structure.check(checker,newValue,newPath)
333
+
334
+ if (isDataValid){
335
+ parent[parentIndex] = newValue
336
+
337
+ await checker.config.save()
338
+ terminal.bold.blue("\n\n✅ Variable saved succesfully!")
339
+ await utilities.timer(400)
340
+ await backFn()
341
+ }else{
342
+ const messages = checker.messages.map((msg) => "=> ["+msg.type.toUpperCase()+"] "+msg.message).join("\n")
343
+ terminal.bold.blue("\n\n❌ Variable is invalid! Please try again!")
344
+ terminal.red("\n"+messages)
345
+ await utilities.timer(1000+(2000*checker.messages.length))
346
+ await this.renderConfigNumberStructureEditor(checker,backFn,structure,data,parent,parentIndex,path,answer)
347
+ }
348
+ }
349
+
350
+ private async renderConfigStringStructureEditor(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),structure:api.ODCheckerStringStructure,data:string,parent:object,parentIndex:string|number,path:(string|number)[],prefillValue?:string){
351
+ if (typeof data != "string") throw new api.ODSystemError("OD CLI => Property is not of the type 'string'. Please check your config for possible errors. (index: "+parentIndex+", path: "+path.join(".")+")")
352
+ renderHeader(this.opts,path)
353
+ terminal(ansis.bold.green("You are now editing "+(typeof parentIndex == "string" ? "the string property "+ansis.blue("\""+parentIndex+"\"") : "string property "+ansis.blue("#"+(parentIndex+1)))+".\n")+ansis.italic.gray("(insert a new value and press enter, go back using escape)\n"))
354
+
355
+ terminal.gray("\nCurrent value:"+(data.includes("\n") ? "\n" : " \"")+ansis.bold.blue(data)+ansis.gray(!data.includes("\n") ? "\"\n" : "\n"))
356
+ terminal.gray("Description: "+ansis.bold(structure.options.cliDisplayDescription ?? "/")+"\n")
357
+
358
+ const customExtraOptions = (structure instanceof api.ODCheckerCustomStructure_DiscordId) ? structure.extraOptions : undefined
359
+ const customAutocompleteFunc = structure.options.cliAutocompleteFunc ? await structure.options.cliAutocompleteFunc() : null
360
+ const autocompleteList = ((customAutocompleteFunc ?? structure.options.cliAutocompleteList) ?? customExtraOptions) ?? structure.options.choices
361
+ const autoCompleteMenuOpts: Terminal.SingleLineMenuOptions = {
362
+ style:terminal.white,
363
+ selectedStyle:terminal.bgBlue.white
364
+ }
365
+
366
+ const input = terminal.inputField({
367
+ default:prefillValue,
368
+ style:terminal.cyan,
369
+ hintStyle:terminal.gray,
370
+ cancelable:false,
371
+ autoComplete:autocompleteList,
372
+ autoCompleteHint:(!!autocompleteList),
373
+ autoCompleteMenu:(autocompleteList) ? autoCompleteMenuOpts as Terminal.Autocompletion : false
374
+ })
375
+
376
+ terminal.on("key",async (name:string,matches:string[],data:object) => {
377
+ if (name == "ESCAPE"){
378
+ terminal.removeListener("key","cli-render-string-structure-edit")
379
+ input.abort()
380
+ await backFn()
381
+ }
382
+ },({id:"cli-render-string-structure-edit"} as any))
383
+
384
+ const answer = await input.promise
385
+ terminal.removeListener("key","cli-render-string-structure-edit")
386
+ if (typeof answer != "string") return
387
+
388
+ //run config checker
389
+ const newValue = answer.replaceAll("\\n","\n")
390
+ const newPath = [...path]
391
+ newPath.shift()
392
+ checker.messages = [] //manually clear previous messages
393
+ const isDataValid = structure.check(checker,newValue,newPath)
394
+
395
+ if (isDataValid){
396
+ parent[parentIndex] = newValue
397
+
398
+ await checker.config.save()
399
+ terminal.bold.blue("\n\n✅ Variable saved succesfully!")
400
+ await utilities.timer(400)
401
+ await backFn()
402
+ }else{
403
+ const messages = checker.messages.map((msg) => "=> ["+msg.type.toUpperCase()+"] "+msg.message).join("\n")
404
+ terminal.bold.blue("\n\n❌ Variable is invalid! Please try again!")
405
+ terminal.red("\n"+messages)
406
+ await utilities.timer(1000+(2000*checker.messages.length))
407
+ await this.renderConfigStringStructureEditor(checker,backFn,structure,data,parent,parentIndex,path,answer)
408
+ }
409
+ }
410
+
411
+ private async renderConfigNullStructureEditor(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),structure:api.ODCheckerNullStructure,data:null,parent:object,parentIndex:string|number,path:(string|number)[]){
412
+ if (data !== null) throw new api.ODSystemError("OD CLI => Property is not of the type 'null'. Please check your config for possible errors. (index: "+parentIndex+", path: "+path.join(".")+")")
413
+ renderHeader(this.opts,path)
414
+ terminal(ansis.bold.green("You are now editing "+(typeof parentIndex == "string" ? "the null property "+ansis.blue("\""+parentIndex+"\"") : "null property "+ansis.blue("#"+(parentIndex+1)))+".\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
415
+
416
+ terminal.gray("\nCurrent value: "+ansis.bold.blue("null")+"\n")
417
+ terminal.gray("Description: "+ansis.bold(structure.options.cliDisplayDescription ?? "/")+"\n")
418
+
419
+ const answer = await terminal.singleColumnMenu(["null"],{
420
+ leftPadding:"> ",
421
+ style:terminal.cyan,
422
+ selectedStyle:terminal.bgDefaultColor.bold,
423
+ submittedStyle:terminal.bgBlue,
424
+ extraLines:2,
425
+ cancelable:true
426
+ }).promise
427
+
428
+ if (answer.canceled) return await backFn()
429
+
430
+ //run config checker
431
+ const newValue = null
432
+ const newPath = [...path]
433
+ newPath.shift()
434
+ checker.messages = [] //manually clear previous messages
435
+ const isDataValid = structure.check(checker,newValue,newPath)
436
+
437
+ if (isDataValid){
438
+ parent[parentIndex] = newValue
439
+
440
+ await checker.config.save()
441
+ terminal.bold.blue("\n\n✅ Variable saved succesfully!")
442
+ await utilities.timer(400)
443
+ await backFn()
444
+ }else{
445
+ const messages = checker.messages.map((msg) => "=> ["+msg.type.toUpperCase()+"] "+msg.message).join("\n")
446
+ terminal.bold.blue("\n\n❌ Variable is invalid! Please try again!")
447
+ terminal.red("\n"+messages)
448
+ await utilities.timer(1000+(2000*checker.messages.length))
449
+ await this.renderConfigNullStructureEditor(checker,backFn,structure,data,parent,parentIndex,path)
450
+ }
451
+ }
452
+
453
+ private async renderConfigTypeSwitchStructureEditor(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),structure:api.ODCheckerTypeSwitchStructure,data:any,parent:object,parentIndex:string|number,path:(string|number)[]){
454
+ renderHeader(this.opts,path)
455
+ terminal(ansis.bold.green("You are now editing "+(typeof parentIndex == "string" ? "the property "+ansis.blue("\""+parentIndex+"\"") : "property "+ansis.blue("#"+(parentIndex+1)))+".\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
456
+
457
+ terminal.gray("\nCurrent value: "+ansis.bold.blue(data.toString())+"\n")
458
+ terminal.gray("Description: "+ansis.bold(structure.options.cliDisplayDescription ?? "/")+"\n")
459
+
460
+ const actionsList: string[] = []
461
+ if (structure.options.boolean) actionsList.push("Edit as boolean")
462
+ if (structure.options.string) actionsList.push("Edit as string")
463
+ if (structure.options.number) actionsList.push("Edit as number")
464
+ if (structure.options.object) actionsList.push("Edit as object")
465
+ if (structure.options.array) actionsList.push("Edit as array/list")
466
+ if (structure.options.null) actionsList.push("Edit as null")
467
+
468
+ const answer = await terminal.singleColumnMenu(actionsList,{
469
+ leftPadding:"> ",
470
+ style:terminal.cyan,
471
+ selectedStyle:terminal.bgDefaultColor.bold,
472
+ submittedStyle:terminal.bgBlue,
473
+ extraLines:2,
474
+ cancelable:true
475
+ }).promise
476
+
477
+ if (answer.canceled) return await backFn()
478
+
479
+ //run selected structure editor (untested)
480
+ if (answer.selectedText.startsWith("Edit as boolean") && structure.options.boolean) await this.renderConfigBooleanStructureEditor(checker,async () => {await this.renderConfigTypeSwitchStructureEditor(checker,backFn,structure,data,parent,parentIndex,path)},structure.options.boolean,false,parent,parentIndex,path)
481
+ else if (answer.selectedText.startsWith("Edit as string") && structure.options.string) await this.renderConfigStringStructureEditor(checker,async () => {await this.renderConfigTypeSwitchStructureEditor(checker,backFn,structure,data,parent,parentIndex,path)},structure.options.string,data.toString(),parent,parentIndex,path)
482
+ else if (answer.selectedText.startsWith("Edit as number") && structure.options.number) await this.renderConfigNumberStructureEditor(checker,async () => {await this.renderConfigTypeSwitchStructureEditor(checker,backFn,structure,data,parent,parentIndex,path)},structure.options.number,0,parent,parentIndex,path)
483
+ else if (answer.selectedText.startsWith("Edit as object") && structure.options.object) await this.renderConfigObjectStructureSelector(checker,async () => {await this.renderConfigTypeSwitchStructureEditor(checker,backFn,structure,data,parent,parentIndex,path)},structure.options.object,data,parent,parentIndex,path)
484
+ else if (answer.selectedText.startsWith("Edit as array/list") && structure.options.array) await this.renderConfigArrayStructureSelector(checker,async () => {await this.renderConfigTypeSwitchStructureEditor(checker,backFn,structure,data,parent,parentIndex,path)},structure.options.array,data,parent,parentIndex,path)
485
+ else if (answer.selectedText.startsWith("Edit as null") && structure.options.null) await this.renderConfigNullStructureEditor(checker,async () => {await this.renderConfigTypeSwitchStructureEditor(checker,backFn,structure,data,parent,parentIndex,path)},structure.options.null,null,parent,parentIndex,path)
486
+ }
487
+
488
+ private getArrayPreviewStructureNameLength(structure:api.ODCheckerStructure,data:api.ODValidJsonType,parent:object,parentIndex:string|number): number {
489
+ if (structure instanceof api.ODCheckerBooleanStructure && typeof data == "boolean") return data.toString().length
490
+ else if (structure instanceof api.ODCheckerNumberStructure && typeof data == "number") return data.toString().length
491
+ else if (structure instanceof api.ODCheckerStringStructure && typeof data == "string") return data.length
492
+ else if (structure instanceof api.ODCheckerNullStructure && data === null) return "Null".length
493
+ else if (structure instanceof api.ODCheckerArrayStructure && Array.isArray(data)) return "Array".length
494
+ else if (structure instanceof api.ODCheckerObjectStructure && typeof data == "object" && !Array.isArray(data) && data){
495
+ if (!structure.options.cliDisplayKeyInParentArray) return "Object".length
496
+ else return data[structure.options.cliDisplayKeyInParentArray].toString().length
497
+
498
+ }else if (structure instanceof api.ODCheckerEnabledObjectStructure && typeof data == "object" && !Array.isArray(data) && data){
499
+ const subStructure = structure.options.checker
500
+ if (!subStructure) return "<unknown-property>".length
501
+ return this.getArrayPreviewStructureNameLength(subStructure,data,parent,parentIndex)
502
+
503
+ }else if (structure instanceof api.ODCheckerObjectSwitchStructure && typeof data == "object" && !Array.isArray(data) && data){
504
+ for (const objectTemplate of (structure.options.objects ?? [])){
505
+ if (objectTemplate.properties.every((prop) => data[prop.key] === prop.value)){
506
+ //object template matches data
507
+ const subStructure = objectTemplate.checker
508
+ return this.getArrayPreviewStructureNameLength(subStructure,data,parent,parentIndex)
509
+ }
510
+ }
511
+ return "<unknown-property>".length
512
+
513
+ }else if (structure instanceof api.ODCheckerTypeSwitchStructure){
514
+ if (typeof data == "boolean" && structure.options.boolean) return this.getArrayPreviewStructureNameLength(structure.options.boolean,data,parent,parentIndex)
515
+ else if (typeof data == "number" && structure.options.number) return this.getArrayPreviewStructureNameLength(structure.options.number,data,parent,parentIndex)
516
+ else if (typeof data == "string" && structure.options.string) return this.getArrayPreviewStructureNameLength(structure.options.string,data,parent,parentIndex)
517
+ else if (typeof data == "object" && !Array.isArray(data) && data && structure.options.object) return this.getArrayPreviewStructureNameLength(structure.options.object,data,parent,parentIndex)
518
+ else if (Array.isArray(data) && structure.options.array) return this.getArrayPreviewStructureNameLength(structure.options.array,data,parent,parentIndex)
519
+ else if (data === null && structure.options.null) return this.getArrayPreviewStructureNameLength(structure.options.null,data,parent,parentIndex)
520
+ else return "<unknown-property>".length
521
+ }else return "<unknown-property>".length
522
+ }
523
+
524
+ private getArrayPreviewFromStructure(structure:api.ODCheckerStructure,data:api.ODValidJsonType,parent:object,parentIndex:string|number,nameLength:number): string {
525
+ if (structure instanceof api.ODCheckerBooleanStructure && typeof data == "boolean") return data.toString()
526
+ else if (structure instanceof api.ODCheckerNumberStructure && typeof data == "number") return data.toString()
527
+ else if (structure instanceof api.ODCheckerStringStructure && typeof data == "string") return data
528
+ else if (structure instanceof api.ODCheckerNullStructure && data === null) return "Null"
529
+ else if (structure instanceof api.ODCheckerArrayStructure && Array.isArray(data)) return "Array"
530
+ else if (structure instanceof api.ODCheckerObjectStructure && typeof data == "object" && !Array.isArray(data) && data){
531
+ const additionalKeys = (structure.options.cliDisplayAdditionalKeysInParentArray ?? []).map((key) => key+": "+data[key].toString()).join(", ")
532
+ if (!structure.options.cliDisplayKeyInParentArray) return "Object"
533
+ else return data[structure.options.cliDisplayKeyInParentArray].toString().padEnd(nameLength+5," ")+ansis.gray(additionalKeys.length > 0 ? "("+additionalKeys+")" : "")
534
+
535
+ }else if (structure instanceof api.ODCheckerEnabledObjectStructure && typeof data == "object" && !Array.isArray(data) && data){
536
+ const subStructure = structure.options.checker
537
+ if (!subStructure) return "<unknown-property>"
538
+ return this.getArrayPreviewFromStructure(subStructure,data,parent,parentIndex,nameLength)
539
+
540
+ }else if (structure instanceof api.ODCheckerObjectSwitchStructure && typeof data == "object" && !Array.isArray(data) && data){
541
+ for (const objectTemplate of (structure.options.objects ?? [])){
542
+ if (objectTemplate.properties.every((prop) => data[prop.key] === prop.value)){
543
+ //object template matches data
544
+ const subStructure = objectTemplate.checker
545
+ return this.getArrayPreviewFromStructure(subStructure,data,parent,parentIndex,nameLength)
546
+ }
547
+ }
548
+ return "<unknown-property>"
549
+
550
+ }else if (structure instanceof api.ODCheckerTypeSwitchStructure){
551
+ if (typeof data == "boolean" && structure.options.boolean) return this.getArrayPreviewFromStructure(structure.options.boolean,data,parent,parentIndex,nameLength)
552
+ else if (typeof data == "number" && structure.options.number) return this.getArrayPreviewFromStructure(structure.options.number,data,parent,parentIndex,nameLength)
553
+ else if (typeof data == "string" && structure.options.string) return this.getArrayPreviewFromStructure(structure.options.string,data,parent,parentIndex,nameLength)
554
+ else if (typeof data == "object" && !Array.isArray(data) && data && structure.options.object) return this.getArrayPreviewFromStructure(structure.options.object,data,parent,parentIndex,nameLength)
555
+ else if (Array.isArray(data) && structure.options.array) return this.getArrayPreviewFromStructure(structure.options.array,data,parent,parentIndex,nameLength)
556
+ else if (data === null && structure.options.null) return this.getArrayPreviewFromStructure(structure.options.null,data,parent,parentIndex,nameLength)
557
+ else return "<unknown-property>"
558
+ }else return "<unknown-property>"
559
+ }
560
+
561
+ private async chooseAdditionConfigStructure(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),nextFn:((data:any) => api.ODPromiseVoid),structure:api.ODCheckerStructure,parent:object|any[],parentIndex:string|number,path:(string|number)[],localPath:(string|number)[]){
562
+ if (structure instanceof api.ODCheckerObjectStructure) await this.renderAdditionConfigObjectStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)
563
+ else if (structure instanceof api.ODCheckerBooleanStructure) await this.renderAdditionConfigBooleanStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)
564
+ else if (structure instanceof api.ODCheckerNumberStructure) await this.renderAdditionConfigNumberStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)
565
+ else if (structure instanceof api.ODCheckerStringStructure) await this.renderAdditionConfigStringStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)
566
+ else if (structure instanceof api.ODCheckerNullStructure) await this.renderAdditionConfigNullStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)
567
+ else if (structure instanceof api.ODCheckerEnabledObjectStructure) await this.renderAdditionConfigEnabledObjectStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)
568
+ else if (structure instanceof api.ODCheckerObjectSwitchStructure) await this.renderAdditionConfigObjectSwitchStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)
569
+ else if (structure instanceof api.ODCheckerArrayStructure) await this.renderAdditionConfigArrayStructureSelector(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)
570
+ else if (structure instanceof api.ODCheckerTypeSwitchStructure) await this.renderAdditionConfigTypeSwitchStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)
571
+ else await backFn()
572
+ }
573
+
574
+ private async renderAdditionConfigObjectStructure(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),nextFn:((data:any) => api.ODPromiseVoid),structure:api.ODCheckerObjectStructure,parent:object|any[],parentIndex:string|number,path:(string|number)[],localPath:(string|number)[],localData:object={}){
575
+ const children = structure.options.children ?? []
576
+ const skipKeys = (structure.options.cliInitSkipKeys ?? [])
577
+ //add skipped properties
578
+ for (const key of skipKeys){
579
+ const childStructure = children.find((c) => c.key == key)
580
+ if (childStructure){
581
+ const defaultValue = childStructure.checker.options.cliInitDefaultValue
582
+ if (childStructure.checker instanceof api.ODCheckerBooleanStructure) localData[key] = (typeof defaultValue == "boolean" ? defaultValue : false)
583
+ else if (childStructure.checker instanceof api.ODCheckerNumberStructure) localData[key] = (typeof defaultValue == "number" ? defaultValue : 0)
584
+ else if (childStructure.checker instanceof api.ODCheckerStringStructure) localData[key] = (typeof defaultValue == "string" ? defaultValue : "")
585
+ else if (childStructure.checker instanceof api.ODCheckerNullStructure) localData[key] = (defaultValue === null ? defaultValue : null)
586
+ else if (childStructure.checker instanceof api.ODCheckerArrayStructure) localData[key] = (Array.isArray(defaultValue) ? JSON.parse(JSON.stringify(defaultValue)) : [])
587
+ else if (childStructure.checker instanceof api.ODCheckerObjectStructure) localData[key] = ((typeof defaultValue == "object" && !Array.isArray(defaultValue) && defaultValue) ? JSON.parse(JSON.stringify(defaultValue)) : {})
588
+ else if (childStructure.checker instanceof api.ODCheckerObjectSwitchStructure) localData[key] = ((typeof defaultValue == "object" && !Array.isArray(defaultValue) && defaultValue) ? JSON.parse(JSON.stringify(defaultValue)) : {})
589
+ else if (childStructure.checker instanceof api.ODCheckerEnabledObjectStructure) localData[key] = ((typeof defaultValue == "object" && !Array.isArray(defaultValue) && defaultValue) ? JSON.parse(JSON.stringify(defaultValue)) : {})
590
+ else if (childStructure.checker instanceof api.ODCheckerTypeSwitchStructure && typeof defaultValue != "undefined") localData[key] = JSON.parse(JSON.stringify(defaultValue))
591
+ else throw new api.ODSystemError("OD CLI => Object skip key has an invalid checker structure! key: "+key)
592
+ }
593
+ }
594
+
595
+ //add properties that need to be configured
596
+ const configChildren = children.filter((c) => !skipKeys.includes(c.key)).map((c) => {return {key:c.key,checker:c.checker}})
597
+ await this.configureAdditionObjectProperties(checker,configChildren,0,localData,[...path,parentIndex],(typeof parentIndex == "number") ? [...localPath] : [...localPath,parentIndex],async () => {
598
+ //go back to previous screen
599
+ await backFn()
600
+ },async () => {
601
+ //finish setup
602
+ terminal.bold.blue("\n\n✅ Variable saved succesfully!")
603
+ await utilities.timer(400)
604
+ await nextFn(localData)
605
+ })
606
+ }
607
+
608
+ private async configureAdditionObjectProperties(checker:api.ODChecker,children:{key:string,checker:api.ODCheckerStructure}[],currentIndex:number,localData:object,path:(string|number)[],localPath:(string|number)[],backFn:(() => api.ODPromiseVoid),nextFn:(() => api.ODPromiseVoid)){
609
+ if (children.length < 1) return await nextFn()
610
+
611
+ const child = children[currentIndex]
612
+ await this.chooseAdditionConfigStructure(checker,async () => {
613
+ if (children[currentIndex-1]) await this.configureAdditionObjectProperties(checker,children,currentIndex-1,localData,path,localPath,backFn,nextFn)
614
+ else await backFn()
615
+ },async (data) => {
616
+ localData[child.key] = data
617
+ if (children[currentIndex+1]) await this.configureAdditionObjectProperties(checker,children,currentIndex+1,localData,path,localPath,backFn,nextFn)
618
+ else await nextFn()
619
+ },child.checker,localData,child.key,path,localPath)
620
+ }
621
+
622
+ private async renderAdditionConfigEnabledObjectStructure(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),nextFn:((data:any) => api.ODPromiseVoid),structure:api.ODCheckerEnabledObjectStructure,parent:object|any[],parentIndex:string|number,path:(string|number)[],localPath:(string|number)[]){
623
+ const enabledProperty = structure.options.property
624
+ const enabledValue = structure.options.enabledValue
625
+ const subStructure = structure.options.checker
626
+ if (!enabledProperty || !subStructure || !subStructure.options.children) return await backFn()
627
+
628
+ let propertyStructure: api.ODCheckerBooleanStructure|api.ODCheckerNumberStructure|api.ODCheckerStringStructure
629
+ if (typeof enabledValue == "string") propertyStructure = new api.ODCheckerStringStructure("opendiscord:CLI-checker-enabled-object-structure",{})
630
+ else if (typeof enabledValue == "number") propertyStructure = new api.ODCheckerNumberStructure("opendiscord:CLI-checker-enabled-object-structure",{})
631
+ else if (typeof enabledValue == "boolean") propertyStructure = new api.ODCheckerBooleanStructure("opendiscord:CLI-checker-enabled-object-structure",{})
632
+ else throw new Error("OD CLI => enabled object structure has an invalid type of enabledProperty. It must be a primitive boolean/number/string.")
633
+
634
+ const localData = {}
635
+ await this.chooseAdditionConfigStructure(checker,backFn,async (data) => {
636
+ if (data === enabledValue) await this.renderAdditionConfigObjectStructure(checker,async () => {await this.renderAdditionConfigEnabledObjectStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)},nextFn,subStructure,parent,parentIndex,path,localPath,localData)
637
+ else{
638
+ localData[enabledProperty] = data
639
+ //copy old object checker to new object checker => all options get de-referenced (this is needed for the new object skip keys are temporary)
640
+ const newStructure = new api.ODCheckerObjectStructure(subStructure.id,{children:[]})
641
+
642
+ //copy all options over to the new checker
643
+ newStructure.options.children = [...subStructure.options.children]
644
+ newStructure.options.cliInitSkipKeys = subStructure.options.children.map((child) => child.key)
645
+ for (const key of Object.keys(subStructure.options)){
646
+ if (key != "children" && key != "cliInitSkipKeys") newStructure.options[key] = subStructure.options[key]
647
+ }
648
+
649
+ //adds all properties to object as "skipKeys", then continues to next function
650
+ await this.renderAdditionConfigObjectStructure(checker,async () => {await this.renderAdditionConfigEnabledObjectStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)},nextFn,newStructure,parent,parentIndex,path,localPath,localData)
651
+ await nextFn(localData)
652
+ }
653
+ },propertyStructure,localData,enabledProperty,[...path,parentIndex],[...localPath,parentIndex])
654
+ }
655
+
656
+ private async renderAdditionConfigObjectSwitchStructure(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),nextFn:((data:any) => api.ODPromiseVoid),structure:api.ODCheckerObjectSwitchStructure,parent:object|any[],parentIndex:string|number,path:(string|number)[],localPath:(string|number)[]){
657
+ renderHeader(this.opts,[...path,parentIndex])
658
+ terminal(ansis.bold.green("What type of object would you like to add?\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
659
+
660
+ const answer = await terminal.singleColumnMenu(structure.options.objects.map((obj) => obj.name),{
661
+ leftPadding:"> ",
662
+ style:terminal.cyan,
663
+ selectedStyle:terminal.bgDefaultColor.bold,
664
+ submittedStyle:terminal.bgBlue,
665
+ extraLines:2,
666
+ cancelable:true
667
+ }).promise
668
+
669
+ if (answer.canceled) return await backFn()
670
+ const objectTemplate = structure.options.objects[answer.selectedIndex]
671
+
672
+ //copy old object checker to new object checker => all options get de-referenced (this is needed for the new object switch properties which are temporary)
673
+ const oldStructure = objectTemplate.checker
674
+ const newStructure = new api.ODCheckerObjectStructure(oldStructure.id,{children:[]})
675
+
676
+ //copy all options over to the new checker
677
+ newStructure.options.children = [...oldStructure.options.children]
678
+ newStructure.options.cliInitSkipKeys = [...(oldStructure.options.cliInitSkipKeys ?? [])]
679
+ for (const key of Object.keys(oldStructure.options)){
680
+ if (key != "children" && key != "cliInitSkipKeys") newStructure.options[key] = oldStructure.options[key]
681
+ }
682
+
683
+ //add the keys of the object switch properties to the 'cliInitSkipKeys' because they need to be skipped.
684
+ objectTemplate.properties.map((p) => p.key).forEach((p) => {
685
+ if (!newStructure.options.cliInitSkipKeys) newStructure.options.cliInitSkipKeys = [p]
686
+ else if (!newStructure.options.cliInitSkipKeys.includes(p)) newStructure.options.cliInitSkipKeys.push(p)
687
+ })
688
+
689
+ //add structure checkers for all properties
690
+ for (const prop of objectTemplate.properties){
691
+ if (!newStructure.options.children.find((child) => child.key === prop.key)){
692
+ if (typeof prop.value == "string") newStructure.options.children.unshift({key:prop.key,optional:false,priority:1,checker:new api.ODCheckerStringStructure("opendiscord:CLI-checker-object-switch-structure",{cliInitDefaultValue:prop.value})})
693
+ else if (typeof prop.value == "number") newStructure.options.children.unshift({key:prop.key,optional:false,priority:1,checker:new api.ODCheckerNumberStructure("opendiscord:CLI-checker-object-switch-structure",{cliInitDefaultValue:prop.value})})
694
+ else if (typeof prop.value == "boolean") newStructure.options.children.unshift({key:prop.key,optional:false,priority:1,checker:new api.ODCheckerBooleanStructure("opendiscord:CLI-checker-object-switch-structure",{cliInitDefaultValue:prop.value})})
695
+ }
696
+ }
697
+
698
+ await this.chooseAdditionConfigStructure(checker,backFn,nextFn,newStructure,parent,parentIndex,path,localPath)
699
+ }
700
+
701
+ private async renderAdditionConfigBooleanStructure(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),nextFn:((data:any) => api.ODPromiseVoid),structure:api.ODCheckerBooleanStructure,parent:object|any[],parentIndex:string|number,path:(string|number)[],localPath:(string|number)[]){
702
+ renderHeader(this.opts,[...path,parentIndex])
703
+ terminal(ansis.bold.green("You are now creating "+(typeof parentIndex == "string" ? "the boolean property "+ansis.blue("\""+parentIndex+"\"") : "boolean property "+ansis.blue("#"+(parentIndex+1)))+".\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
704
+
705
+ terminal.gray("\nProperty: "+ansis.bold.blue(structure.options.cliDisplayName ?? [...localPath,parentIndex].join("."))+"\n")
706
+ terminal.gray("Description: "+ansis.bold(structure.options.cliDisplayDescription ?? "/")+"\n")
707
+
708
+ const answer = await terminal.singleColumnMenu(["false (Disabled)","true (Enabled)"],{
709
+ leftPadding:"> ",
710
+ style:terminal.cyan,
711
+ selectedStyle:terminal.bgDefaultColor.bold,
712
+ submittedStyle:terminal.bgBlue,
713
+ extraLines:2,
714
+ cancelable:true
715
+ }).promise
716
+
717
+ if (answer.canceled) return await backFn()
718
+
719
+ //run config checker
720
+ const newValue = (answer.selectedIndex == 0) ? false : true
721
+ const newPath = [...path]
722
+ newPath.shift()
723
+ checker.messages = [] //manually clear previous messages
724
+ const isDataValid = structure.check(checker,newValue,newPath)
725
+
726
+ if (isDataValid){
727
+ terminal.bold.blue("\n\n✅ Variable saved succesfully!")
728
+ await utilities.timer(400)
729
+ await nextFn(newValue)
730
+ }else{
731
+ const messages = checker.messages.map((msg) => "=> ["+msg.type.toUpperCase()+"] "+msg.message).join("\n")
732
+ terminal.bold.blue("\n\n❌ Variable is invalid! Please try again!")
733
+ terminal.gray("\n"+messages)
734
+ await utilities.timer(1000+(2000*checker.messages.length))
735
+ await this.renderAdditionConfigBooleanStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)
736
+ }
737
+ }
738
+
739
+ private async renderAdditionConfigNumberStructure(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),nextFn:((data:any) => api.ODPromiseVoid),structure:api.ODCheckerNumberStructure,parent:object|any[],parentIndex:string|number,path:(string|number)[],localPath:(string|number)[],prefillValue?:string){
740
+ renderHeader(this.opts,[...path,parentIndex])
741
+ terminal(ansis.bold.green("You are now creating "+(typeof parentIndex == "string" ? "the number property "+ansis.blue("\""+parentIndex+"\"") : "number property "+ansis.blue("#"+(parentIndex+1)))+".\n")+ansis.italic.gray("(insert a new value and press enter, go back using escape)\n"))
742
+
743
+ terminal.gray("\nProperty: "+ansis.bold.blue(structure.options.cliDisplayName ?? [...localPath,parentIndex].join("."))+"\n")
744
+ terminal.gray("Description: "+ansis.bold(structure.options.cliDisplayDescription ?? "/")+"\n")
745
+
746
+ const answer = await terminal.inputField({
747
+ default:prefillValue,
748
+ style:terminal.cyan,
749
+ cancelable:true
750
+ }).promise
751
+
752
+ if (typeof answer != "string") return await backFn()
753
+
754
+ //run config checker
755
+ const newValue = Number(answer.replaceAll(",","."))
756
+ const newPath = [...path]
757
+ newPath.shift()
758
+ checker.messages = [] //manually clear previous messages
759
+ const isDataValid = structure.check(checker,newValue,newPath)
760
+
761
+ if (isDataValid){
762
+ terminal.bold.blue("\n\n✅ Variable saved succesfully!")
763
+ await utilities.timer(400)
764
+ await nextFn(newValue)
765
+ }else{
766
+ const messages = checker.messages.map((msg) => "=> ["+msg.type.toUpperCase()+"] "+msg.message).join("\n")
767
+ terminal.bold.blue("\n\n❌ Variable is invalid! Please try again!")
768
+ terminal.red("\n"+messages)
769
+ await utilities.timer(1000+(2000*checker.messages.length))
770
+ await this.renderAdditionConfigNumberStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath,answer)
771
+ }
772
+ }
773
+
774
+ private async renderAdditionConfigStringStructure(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),nextFn:((data:any) => api.ODPromiseVoid),structure:api.ODCheckerStringStructure,parent:object|any[],parentIndex:string|number,path:(string|number)[],localPath:(string|number)[],prefillValue?:string){
775
+ renderHeader(this.opts,[...path,parentIndex])
776
+ terminal(ansis.bold.green("You are now creating "+(typeof parentIndex == "string" ? "the string property "+ansis.blue("\""+parentIndex+"\"") : "string property "+ansis.blue("#"+(parentIndex+1)))+".\n")+ansis.italic.gray("(insert a new value and press enter, go back using escape)\n"))
777
+
778
+ terminal.gray("\nProperty: "+ansis.bold.blue(structure.options.cliDisplayName ?? [...localPath,parentIndex].join("."))+"\n")
779
+ terminal.gray("Description: "+ansis.bold(structure.options.cliDisplayDescription ?? "/")+"\n")
780
+
781
+ const customExtraOptions = (structure instanceof api.ODCheckerCustomStructure_DiscordId) ? structure.extraOptions : undefined
782
+ const customAutocompleteFunc = structure.options.cliAutocompleteFunc ? await structure.options.cliAutocompleteFunc() : null
783
+ const autocompleteList = ((customAutocompleteFunc ?? structure.options.cliAutocompleteList) ?? customExtraOptions) ?? structure.options.choices
784
+ const autoCompleteMenuOpts: Terminal.SingleLineMenuOptions = {
785
+ style:terminal.white,
786
+ selectedStyle:terminal.bgBlue.white
787
+ }
788
+
789
+ const input = terminal.inputField({
790
+ default:prefillValue,
791
+ style:terminal.cyan,
792
+ hintStyle:terminal.gray,
793
+ cancelable:false,
794
+ autoComplete:autocompleteList,
795
+ autoCompleteHint:(!!autocompleteList),
796
+ autoCompleteMenu:(autocompleteList) ? autoCompleteMenuOpts as Terminal.Autocompletion : false
797
+ })
798
+
799
+ terminal.on("key",async (name:string,matches:string[],data:object) => {
800
+ if (name == "ESCAPE"){
801
+ terminal.removeListener("key","cli-render-string-structure-add")
802
+ input.abort()
803
+ await backFn()
804
+ }
805
+ },({id:"cli-render-string-structure-add"} as any))
806
+
807
+ const answer = await input.promise
808
+ terminal.removeListener("key","cli-render-string-structure-add")
809
+ if (typeof answer != "string") return
810
+
811
+ //run config checker
812
+ const newValue = answer.replaceAll("\\n","\n")
813
+ const newPath = [...path]
814
+ newPath.shift()
815
+ checker.messages = [] //manually clear previous messages
816
+ const isDataValid = structure.check(checker,newValue,newPath)
817
+
818
+ if (isDataValid){
819
+ terminal.bold.blue("\n\n✅ Variable saved succesfully!")
820
+ await utilities.timer(400)
821
+ await nextFn(newValue)
822
+ }else{
823
+ const messages = checker.messages.map((msg) => "=> ["+msg.type.toUpperCase()+"] "+msg.message).join("\n")
824
+ terminal.bold.blue("\n\n❌ Variable is invalid! Please try again!")
825
+ terminal.red("\n"+messages)
826
+ await utilities.timer(1000+(2000*checker.messages.length))
827
+ await this.renderAdditionConfigStringStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath,answer)
828
+ }
829
+ }
830
+
831
+ private async renderAdditionConfigNullStructure(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),nextFn:((data:any) => api.ODPromiseVoid),structure:api.ODCheckerNullStructure,parent:object|any[],parentIndex:string|number,path:(string|number)[],localPath:(string|number)[]){
832
+ renderHeader(this.opts,[...path,parentIndex])
833
+ terminal(ansis.bold.green("You are now creating "+(typeof parentIndex == "string" ? "the null property "+ansis.blue("\""+parentIndex+"\"") : "null property "+ansis.blue("#"+(parentIndex+1)))+".\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
834
+
835
+ terminal.gray("\nProperty: "+ansis.bold.blue(structure.options.cliDisplayName ?? [...localPath,parentIndex].join("."))+"\n")
836
+ terminal.gray("Description: "+ansis.bold(structure.options.cliDisplayDescription ?? "/")+"\n")
837
+
838
+ const answer = await terminal.singleColumnMenu(["null"],{
839
+ leftPadding:"> ",
840
+ style:terminal.cyan,
841
+ selectedStyle:terminal.bgDefaultColor.bold,
842
+ submittedStyle:terminal.bgBlue,
843
+ extraLines:2,
844
+ cancelable:true
845
+ }).promise
846
+
847
+ if (answer.canceled) return await backFn()
848
+
849
+ //run config checker
850
+ const newValue = null
851
+ const newPath = [...path]
852
+ newPath.shift()
853
+ checker.messages = [] //manually clear previous messages
854
+ const isDataValid = structure.check(checker,newValue,newPath)
855
+
856
+ if (isDataValid){
857
+ terminal.bold.blue("\n\n✅ Variable saved succesfully!")
858
+ await utilities.timer(400)
859
+ await nextFn(newValue)
860
+ }else{
861
+ const messages = checker.messages.map((msg) => "=> ["+msg.type.toUpperCase()+"] "+msg.message).join("\n")
862
+ terminal.bold.blue("\n\n❌ Variable is invalid! Please try again!")
863
+ terminal.red("\n"+messages)
864
+ await utilities.timer(1000+(2000*checker.messages.length))
865
+ await this.renderAdditionConfigNullStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)
866
+ }
867
+ }
868
+
869
+ private async renderAdditionConfigArrayStructureSelector(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),nextFn:((data:any) => api.ODPromiseVoid),structure:api.ODCheckerArrayStructure,parent:object|any[],parentIndex:string|number,path:(string|number)[],localPath:(string|number)[],localData:any[]=[]){
870
+ renderHeader(this.opts,[...path,parentIndex])
871
+ terminal(ansis.bold.green("Please select what you would like to do with the new array.\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
872
+ if (!structure.options.propertyChecker) return await backFn()
873
+
874
+ terminal.gray("\nProperty: "+ansis.bold.blue(structure.options.cliDisplayName ?? [...localPath,parentIndex].join("."))+"\n")
875
+ terminal.gray("Description: "+ansis.bold(structure.options.cliDisplayDescription ?? "/")+"\n")
876
+
877
+ const propertyName = structure.options.cliDisplayPropertyName ?? "index"
878
+ const answer = await terminal.singleColumnMenu(localData.length < 1 ? [ansis.magenta("-> Continue to next variable"),"Add "+propertyName] : [
879
+ ansis.magenta("-> Continue to next variable"),
880
+ "Add "+propertyName,
881
+ "Edit "+propertyName,
882
+ "Move "+propertyName,
883
+ "Remove "+propertyName,
884
+ "Duplicate "+propertyName,
885
+
886
+ ],{
887
+ leftPadding:"> ",
888
+ style:terminal.cyan,
889
+ selectedStyle:terminal.bgDefaultColor.bold,
890
+ submittedStyle:terminal.bgBlue,
891
+ extraLines:2,
892
+ cancelable:true
893
+ }).promise
894
+
895
+ const backFnFunc = async () => {await this.renderAdditionConfigArrayStructureSelector(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath,localData)}
896
+
897
+ if (answer.canceled) return await backFn()
898
+ if (answer.selectedIndex == 0) await nextFn(localData)
899
+ else if (answer.selectedIndex == 1) await this.chooseAdditionConfigStructure(checker,backFnFunc,async (newData) => {
900
+ localData[localData.length] = newData
901
+ await backFnFunc()
902
+ },structure.options.propertyChecker,localData,localData.length,path,[])
903
+ else if (answer.selectedIndex == 2) await this.renderConfigArrayStructureEditSelector(checker,backFnFunc,structure,structure.options.propertyChecker,localData,parent,parentIndex,path)
904
+ else if (answer.selectedIndex == 3) await this.renderConfigArrayStructureMoveSelector(checker,backFnFunc,structure,structure.options.propertyChecker,localData,parent,parentIndex,path)
905
+ else if (answer.selectedIndex == 4) await this.renderConfigArrayStructureRemoveSelector(checker,backFnFunc,structure,structure.options.propertyChecker,localData,parent,parentIndex,path)
906
+ else if (answer.selectedIndex == 5) await this.renderConfigArrayStructureDuplicateSelector(checker,backFnFunc,structure,structure.options.propertyChecker,localData,parent,parentIndex,path)
907
+ }
908
+
909
+ private async renderAdditionConfigTypeSwitchStructure(checker:api.ODChecker,backFn:(() => api.ODPromiseVoid),nextFn:((data:any) => api.ODPromiseVoid),structure:api.ODCheckerTypeSwitchStructure,parent:object|any[],parentIndex:string|number,path:(string|number)[],localPath:(string|number)[]){
910
+ renderHeader(this.opts,path)
911
+ terminal(ansis.bold.green("You are now creating "+(typeof parentIndex == "string" ? "the property "+ansis.blue("\""+parentIndex+"\"") : "property "+ansis.blue("#"+(parentIndex+1)))+".\n")+ansis.italic.gray("(use arrow keys to navigate, go back using escape)\n"))
912
+
913
+ terminal.gray("\nProperty: "+ansis.bold.blue(structure.options.cliDisplayName ?? [...localPath,parentIndex].join("."))+"\n")
914
+ terminal.gray("Description: "+ansis.bold(structure.options.cliDisplayDescription ?? "/")+"\n")
915
+
916
+ const actionsList: string[] = []
917
+ if (structure.options.boolean) actionsList.push("Create as boolean")
918
+ if (structure.options.string) actionsList.push("Create as string")
919
+ if (structure.options.number) actionsList.push("Create as number")
920
+ if (structure.options.object) actionsList.push("Create as object")
921
+ if (structure.options.array) actionsList.push("Create as array/list")
922
+ if (structure.options.null) actionsList.push("Create as null")
923
+
924
+ const answer = await terminal.singleColumnMenu(actionsList,{
925
+ leftPadding:"> ",
926
+ style:terminal.cyan,
927
+ selectedStyle:terminal.bgDefaultColor.bold,
928
+ submittedStyle:terminal.bgBlue,
929
+ extraLines:2,
930
+ cancelable:true
931
+ }).promise
932
+
933
+ if (answer.canceled) return await backFn()
934
+
935
+ //run selected structure editor (untested)
936
+ if (answer.selectedText.startsWith("Create as boolean") && structure.options.boolean) await this.renderAdditionConfigBooleanStructure(checker,async () => {await this.renderAdditionConfigTypeSwitchStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)},nextFn,structure.options.boolean,parent,parentIndex,path,localPath)
937
+ else if (answer.selectedText.startsWith("Create as string") && structure.options.string) await this.renderAdditionConfigStringStructure(checker,async () => {await this.renderAdditionConfigTypeSwitchStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)},nextFn,structure.options.string,parent,parentIndex,path,localPath)
938
+ else if (answer.selectedText.startsWith("Create as number") && structure.options.number) await this.renderAdditionConfigNumberStructure(checker,async () => {await this.renderAdditionConfigTypeSwitchStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)},nextFn,structure.options.number,parent,parentIndex,path,localPath)
939
+ else if (answer.selectedText.startsWith("Create as object") && structure.options.object) await this.renderAdditionConfigObjectStructure(checker,async () => {await this.renderAdditionConfigTypeSwitchStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)},nextFn,structure.options.object,parent,parentIndex,path,localPath)
940
+ else if (answer.selectedText.startsWith("Create as array/list") && structure.options.array) await this.renderAdditionConfigArrayStructureSelector(checker,async () => {await this.renderAdditionConfigTypeSwitchStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)},nextFn,structure.options.array,parent,parentIndex,path,localPath)
941
+ else if (answer.selectedText.startsWith("Create as null") && structure.options.null) await this.renderAdditionConfigNullStructure(checker,async () => {await this.renderAdditionConfigTypeSwitchStructure(checker,backFn,nextFn,structure,parent,parentIndex,path,localPath)},nextFn,structure.options.null,parent,parentIndex,path,localPath)
942
+ }
943
+ }