@nan0web/ui 1.0.2 → 1.0.4
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.
- package/README.md +19 -54
- package/package.json +21 -17
- package/src/App/Core/CoreApp.js +14 -19
- package/src/App/Core/UI.js +4 -50
- package/src/App/Core/Widget.js +4 -6
- package/src/App/User/Command/Message.js +23 -37
- package/src/App/User/Command/index.js +3 -8
- package/src/App/User/UserApp.js +32 -27
- package/src/App/User/UserUI.js +4 -4
- package/src/App/User/index.js +0 -6
- package/src/App/index.js +0 -3
- package/src/README.md.js +33 -57
- package/src/StdIn.js +12 -15
- package/src/View/View.js +5 -5
- package/src/core/Error/index.js +9 -0
- package/src/core/Form/Form.js +43 -23
- package/src/core/Form/Input.js +16 -7
- package/src/core/Form/Message.js +6 -8
- package/src/core/InputAdapter.js +6 -2
- package/src/core/Message/Message.js +109 -19
- package/src/core/Message/OutputMessage.js +8 -8
- package/src/core/Message/index.js +3 -4
- package/src/core/Stream.js +10 -10
- package/src/core/UiAdapter.js +189 -0
- package/src/core/index.js +5 -6
- package/src/index.js +5 -4
- package/types/App/Core/CoreApp.d.ts +9 -9
- package/types/App/Core/UI.d.ts +5 -15
- package/types/App/Core/Widget.d.ts +7 -8
- package/types/App/User/Command/Message.d.ts +15 -29
- package/types/App/User/Command/Options.d.ts +9 -2
- package/types/App/User/Command/index.d.ts +3 -7
- package/types/App/User/UserApp.d.ts +19 -9
- package/types/App/User/UserUI.d.ts +0 -9
- package/types/App/User/index.d.ts +0 -4
- package/types/App/index.d.ts +1 -3
- package/types/Frame/Frame.d.ts +5 -5
- package/types/StdIn.d.ts +13 -13
- package/types/View/View.d.ts +9 -9
- package/types/core/Error/index.d.ts +6 -0
- package/types/core/Form/Form.d.ts +14 -11
- package/types/core/Form/Input.d.ts +20 -7
- package/types/core/Form/Message.d.ts +5 -4
- package/types/core/InputAdapter.d.ts +2 -0
- package/types/core/Intent.d.ts +91 -0
- package/types/core/Message/InputMessage.d.ts +5 -5
- package/types/core/Message/Message.d.ts +58 -15
- package/types/core/Message/OutputMessage.d.ts +4 -4
- package/types/core/Message/index.d.ts +3 -4
- package/types/core/Stream.d.ts +5 -4
- package/types/core/StreamEntry.d.ts +1 -1
- package/types/core/UiAdapter.d.ts +104 -0
- package/types/core/index.d.ts +3 -3
- package/types/index.d.ts +5 -4
- package/src/App/Command/Options.js +0 -78
- package/src/App/Command/index.js +0 -9
- package/src/App/User/Command/Options.js +0 -48
- package/src/core/Message/InputMessage.js +0 -119
|
@@ -1,12 +1,36 @@
|
|
|
1
|
-
import { Message
|
|
1
|
+
import { Message } from "@nan0web/co"
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* @typedef {Object} MessageBodySchema
|
|
5
|
+
* @property {boolean} [required]
|
|
6
|
+
* @property {string} [help]
|
|
7
|
+
* @property {RegExp} [pattern]
|
|
8
|
+
* @property {string[]} [options]
|
|
9
|
+
* @property {*} [defaultValue]
|
|
10
|
+
* @property {Function} [validate]
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Base message class for UI communications.
|
|
15
|
+
* A message holds structured data (body) defined by a static Body class.
|
|
16
|
+
* It can represent commands, forms, alerts, or any UI unit.
|
|
17
|
+
*
|
|
18
|
+
* @class UiMessage
|
|
19
|
+
* @extends Message
|
|
5
20
|
*
|
|
6
|
-
* @
|
|
7
|
-
*
|
|
21
|
+
* @example
|
|
22
|
+
* class UserLoginMessage extends UiMessage {
|
|
23
|
+
* static Body = class {
|
|
24
|
+
* static username = { required: true, help: "Enter username" }
|
|
25
|
+
* static password = { required: true, type: "password" }
|
|
26
|
+
* constructor({ username = "", password = "" }) {
|
|
27
|
+
* this.username = username
|
|
28
|
+
* this.password = password
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
8
32
|
*/
|
|
9
|
-
class
|
|
33
|
+
export default class UiMessage extends Message {
|
|
10
34
|
static TYPES = {
|
|
11
35
|
TEXT: 'text',
|
|
12
36
|
FORM: 'form',
|
|
@@ -25,7 +49,7 @@ class UIMessage extends BaseMessage {
|
|
|
25
49
|
id = ""
|
|
26
50
|
|
|
27
51
|
/**
|
|
28
|
-
* Creates a
|
|
52
|
+
* Creates a UiMessage.
|
|
29
53
|
*
|
|
30
54
|
* @param {Object} [input={}] - Message properties.
|
|
31
55
|
*/
|
|
@@ -45,14 +69,62 @@ class UIMessage extends BaseMessage {
|
|
|
45
69
|
}
|
|
46
70
|
|
|
47
71
|
/**
|
|
48
|
-
*
|
|
72
|
+
* Checks whether the message contains any body content.
|
|
49
73
|
*
|
|
50
|
-
* @
|
|
51
|
-
* @returns {UIMessage}
|
|
74
|
+
* @returns {boolean}
|
|
52
75
|
*/
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
76
|
+
get empty() {
|
|
77
|
+
return !this.body
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Validates the message body against its schema.
|
|
82
|
+
*
|
|
83
|
+
* NOTE: The signature must exactly match `Message.validate` – it returns a
|
|
84
|
+
* `Map<string,string>` regardless of the generic type, otherwise TypeScript
|
|
85
|
+
* reports incompatibility with the base class.
|
|
86
|
+
*
|
|
87
|
+
* @param {any} [body=this.body] - Optional body to validate.
|
|
88
|
+
* @returns {Map<string,string>} Map of validation errors, empty if valid.
|
|
89
|
+
*/
|
|
90
|
+
validate(body = this.body) {
|
|
91
|
+
/** @type {any} */
|
|
92
|
+
const Class = /** @type {typeof Message} */ (this.constructor).Body
|
|
93
|
+
const result = new Map()
|
|
94
|
+
const entries = /** @type {Array<[string, MessageBodySchema]>} */ (Object.entries(Class))
|
|
95
|
+
|
|
96
|
+
for (const [field, schema] of entries) {
|
|
97
|
+
const value = body[field]
|
|
98
|
+
const fn = schema?.validate
|
|
99
|
+
if ("function" === typeof fn) {
|
|
100
|
+
const ok = fn.apply(body, [value])
|
|
101
|
+
if (ok !== true) {
|
|
102
|
+
result.set(field, String(ok))
|
|
103
|
+
continue
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const required = schema?.required ?? false
|
|
107
|
+
if (required && !value) {
|
|
108
|
+
result.set(field, "Required")
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
if (schema?.pattern && schema.pattern instanceof RegExp) {
|
|
112
|
+
if (!schema.pattern.test(value)) {
|
|
113
|
+
result.set(field, `Does not match pattern: ${schema.pattern}`)
|
|
114
|
+
continue
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (schema?.options) {
|
|
118
|
+
if (!Array.isArray(schema.options)) {
|
|
119
|
+
throw new Error("Schema options must be an array of possible values")
|
|
120
|
+
}
|
|
121
|
+
if (!schema.options.includes(value)) {
|
|
122
|
+
result.set(field, "Enumeration must have one value")
|
|
123
|
+
continue
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return result
|
|
56
128
|
}
|
|
57
129
|
|
|
58
130
|
/**
|
|
@@ -61,17 +133,35 @@ class UIMessage extends BaseMessage {
|
|
|
61
133
|
* @returns {boolean}
|
|
62
134
|
*/
|
|
63
135
|
isValidType() {
|
|
64
|
-
return Object.values(
|
|
136
|
+
return Object.values(UiMessage.TYPES).includes(this.type)
|
|
65
137
|
}
|
|
66
138
|
|
|
67
139
|
/**
|
|
68
|
-
*
|
|
140
|
+
* Creates a UiMessage instance from plain data.
|
|
69
141
|
*
|
|
70
|
-
* @
|
|
142
|
+
* @param {Object} data - Message data.
|
|
143
|
+
* @returns {UiMessage}
|
|
71
144
|
*/
|
|
72
|
-
|
|
73
|
-
|
|
145
|
+
static from(data) {
|
|
146
|
+
if (data instanceof UiMessage) return data
|
|
147
|
+
return new this(data)
|
|
74
148
|
}
|
|
75
|
-
}
|
|
76
149
|
|
|
77
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Initializes body from input using static Body schema.
|
|
152
|
+
*
|
|
153
|
+
* @param {Object} input - Input object.
|
|
154
|
+
* @param {Function} BodyClass - Static body class with defaults and schema.
|
|
155
|
+
* @returns {Object} Parsed body.
|
|
156
|
+
*/
|
|
157
|
+
static parseBody(input = {}, BodyClass) {
|
|
158
|
+
const result = {}
|
|
159
|
+
const entries = /** @type {Array<[string, MessageBodySchema]>} */ (Object.entries(BodyClass))
|
|
160
|
+
|
|
161
|
+
for (const [field, schema] of entries) {
|
|
162
|
+
const { defaultValue = undefined, ...schemaProps } = schema
|
|
163
|
+
result[field] = input[field] ?? defaultValue
|
|
164
|
+
}
|
|
165
|
+
return result
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import UiMessage from "./Message.js"
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* OutputMessage – message sent from the system to the UI.
|
|
5
5
|
*
|
|
6
6
|
* @class OutputMessage
|
|
7
|
-
* @extends
|
|
7
|
+
* @extends UiMessage
|
|
8
8
|
*/
|
|
9
|
-
export default class OutputMessage extends
|
|
9
|
+
export default class OutputMessage extends UiMessage {
|
|
10
10
|
static PRIORITY = {
|
|
11
11
|
LOW: 0,
|
|
12
12
|
NORMAL: 1,
|
|
@@ -52,9 +52,9 @@ export default class OutputMessage extends UIMessage {
|
|
|
52
52
|
this.priority = Number(priority)
|
|
53
53
|
|
|
54
54
|
if (!this.type && this.error) {
|
|
55
|
-
this.type =
|
|
55
|
+
this.type = UiMessage.TYPES.ERROR
|
|
56
56
|
} else if (!this.type) {
|
|
57
|
-
this.type =
|
|
57
|
+
this.type = UiMessage.TYPES.INFO
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -72,11 +72,11 @@ export default class OutputMessage extends UIMessage {
|
|
|
72
72
|
}
|
|
73
73
|
/** @returns {boolean} */
|
|
74
74
|
get isError() {
|
|
75
|
-
return this.error !== null || this.type ===
|
|
75
|
+
return this.error !== null || this.type === UiMessage.TYPES.ERROR
|
|
76
76
|
}
|
|
77
77
|
/** @returns {boolean} */
|
|
78
78
|
get isInfo() {
|
|
79
|
-
return this.type ===
|
|
79
|
+
return this.type === UiMessage.TYPES.INFO || this.type === UiMessage.TYPES.SUCCESS
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/**
|
|
@@ -140,4 +140,4 @@ export default class OutputMessage extends UIMessage {
|
|
|
140
140
|
if (input instanceof OutputMessage) return input
|
|
141
141
|
return new OutputMessage(input)
|
|
142
142
|
}
|
|
143
|
-
}
|
|
143
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import InputMessage from "./InputMessage.js"
|
|
1
|
+
import UiMessage from "./Message.js"
|
|
3
2
|
import OutputMessage from "./OutputMessage.js"
|
|
4
3
|
|
|
5
|
-
export {
|
|
4
|
+
export { UiMessage, OutputMessage }
|
|
6
5
|
|
|
7
|
-
export default
|
|
6
|
+
export default UiMessage
|
package/src/core/Stream.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import StreamEntry from "./StreamEntry.js"
|
|
2
2
|
|
|
3
|
+
export { StreamEntry } // Export if needed
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
|
-
* Agnostic UI stream for processing progress.
|
|
6
|
+
* Agnostic UI stream for processing progress using async generators.
|
|
5
7
|
*
|
|
6
8
|
* @class UIStream
|
|
7
9
|
*/
|
|
@@ -29,17 +31,17 @@ export default class UIStream {
|
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
|
-
* Runs
|
|
34
|
+
* Runs an async generator with progress callbacks and abort handling.
|
|
33
35
|
*
|
|
34
36
|
* @param {AbortSignal} signal - Abort signal.
|
|
35
|
-
* @param {
|
|
37
|
+
* @param {() => AsyncGenerator<StreamEntry>} generatorFn - Function that returns an async generator.
|
|
36
38
|
* @param {Function} [onProgress] - Called with (progress, item).
|
|
37
39
|
* @param {Function} [onError] - Called with (errorMessage, item).
|
|
38
40
|
* @param {Function} [onComplete] - Called with (item) when done.
|
|
39
41
|
* @returns {Promise<void>}
|
|
40
42
|
*/
|
|
41
|
-
static async process(signal,
|
|
42
|
-
const iter =
|
|
43
|
+
static async process(signal, generatorFn, onProgress, onError, onComplete) {
|
|
44
|
+
const iter = generatorFn()
|
|
43
45
|
|
|
44
46
|
try {
|
|
45
47
|
for await (const item of iter) {
|
|
@@ -47,13 +49,11 @@ export default class UIStream {
|
|
|
47
49
|
throw new DOMException('Aborted', 'AbortError')
|
|
48
50
|
}
|
|
49
51
|
|
|
50
|
-
if (item.
|
|
51
|
-
onProgress?.(item.progress, item)
|
|
52
|
-
} else if (item.error) {
|
|
53
|
-
onError?.(item.error, item)
|
|
54
|
-
} else if (item.done) {
|
|
52
|
+
if (item.done) {
|
|
55
53
|
onComplete?.(item)
|
|
56
54
|
break
|
|
55
|
+
} else if (item.error) {
|
|
56
|
+
onError?.(item.error, item)
|
|
57
57
|
} else {
|
|
58
58
|
// Intermediate results
|
|
59
59
|
onProgress?.(null, item)
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import EventProcessor from "@nan0web/event/oop"
|
|
2
|
+
import CancelError from "./Error/CancelError.js"
|
|
3
|
+
import UIMessage from "./Message/Message.js"
|
|
4
|
+
import UIForm from "./Form/Form.js"
|
|
5
|
+
import FormInput from "./Form/Input.js"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Unified UI Adapter that handles both input and output operations.
|
|
9
|
+
* It manages user interactions and rendering of messages, forms, and progress.
|
|
10
|
+
*
|
|
11
|
+
* @class UiAdapter
|
|
12
|
+
* @extends EventProcessor
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const adapter = new UiAdapter()
|
|
16
|
+
* adapter.output = new View()
|
|
17
|
+
*
|
|
18
|
+
* const result = await adapter.requireInput(new LoginMessage())
|
|
19
|
+
* console.log(result) // { username: "user", password: "pass" }
|
|
20
|
+
*/
|
|
21
|
+
export default class UiAdapter extends EventProcessor {
|
|
22
|
+
static CancelError = CancelError
|
|
23
|
+
/** @returns {typeof CancelError} */
|
|
24
|
+
get CancelError() {
|
|
25
|
+
return /** @type {typeof UiAdapter} */ (this.constructor).CancelError
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @type {OutputAdapter | null} Output interface for rendering */
|
|
29
|
+
output = null
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Starts listening for input and emits an `input` event.
|
|
33
|
+
*
|
|
34
|
+
* @returns {void}
|
|
35
|
+
*/
|
|
36
|
+
start() {
|
|
37
|
+
this.emit('input', UIMessage.from({ body: "Adapter started" }))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Stops listening for input and output streams.
|
|
42
|
+
* Default implementation does nothing; override in subclasses to perform cleanup.
|
|
43
|
+
*
|
|
44
|
+
* @returns {void}
|
|
45
|
+
*/
|
|
46
|
+
stop() {
|
|
47
|
+
// Default implementation – does nothing
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Checks whether the adapter is ready to receive input.
|
|
52
|
+
*
|
|
53
|
+
* @returns {boolean} Always true in base class; override for specific checks.
|
|
54
|
+
*/
|
|
55
|
+
isReady() {
|
|
56
|
+
return true
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Helper to ask a question.
|
|
61
|
+
* Must be implemented by subclasses.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} question - Question to ask the user.
|
|
64
|
+
* @returns {Promise<string>} User's response.
|
|
65
|
+
* @throws {Error} If not implemented in subclass.
|
|
66
|
+
*/
|
|
67
|
+
async ask(question) {
|
|
68
|
+
throw new Error('ask() method must be implemented in subclass')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Generic selection prompt.
|
|
73
|
+
* Must be implemented by subclasses.
|
|
74
|
+
*
|
|
75
|
+
* @param {object} config - Selection configuration.
|
|
76
|
+
* @param {string[]} [config.options=[]] - List of options to choose from.
|
|
77
|
+
* @returns {Promise<{ index: number, value: string | null }>} Selected option.
|
|
78
|
+
* @throws {Error} If not implemented in subclass.
|
|
79
|
+
*/
|
|
80
|
+
async select(config) {
|
|
81
|
+
throw new Error('select() method must be implemented in subclass')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Ensures a message's body is fully and validly filled.
|
|
86
|
+
* Generates a form from the message's static Body schema,
|
|
87
|
+
* then iteratively collects input until all fields are valid or cancelled.
|
|
88
|
+
*
|
|
89
|
+
* @template {UIMessage} T
|
|
90
|
+
* @param {T} msg - Message instance needing input.
|
|
91
|
+
* @returns {Promise<T['body']>} Updated and validated message body.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* const body = await adapter.requireInput(new LoginMessage({ body: { username: "user" } }))
|
|
95
|
+
* // → prompts for password, returns { username: "user", password: "..." }
|
|
96
|
+
*/
|
|
97
|
+
async requireInput(msg) {
|
|
98
|
+
if (!msg) {
|
|
99
|
+
throw new Error("Message instance is required")
|
|
100
|
+
}
|
|
101
|
+
if (!(msg instanceof UIMessage)) {
|
|
102
|
+
throw new TypeError("Message must be an instance of UIMessage")
|
|
103
|
+
}
|
|
104
|
+
/** @type {Map<string,string>} */
|
|
105
|
+
let errors = msg.validate()
|
|
106
|
+
while (errors.size > 0) {
|
|
107
|
+
const form = generateForm(
|
|
108
|
+
/** @type {any} */ (msg.constructor).Body,
|
|
109
|
+
{ initialState: msg.body }
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
const formResult = await this.processForm ? this.processForm(form, msg.body) : {} // Assume method exists or handle differently, but error indicates missing method; perhaps remove if not used
|
|
113
|
+
if (formResult.escaped) {
|
|
114
|
+
throw new CancelError("User cancelled form")
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const updatedBody = { ...msg.body, ...formResult.form.state }
|
|
118
|
+
const updatedErrors = msg.validate(updatedBody)
|
|
119
|
+
|
|
120
|
+
if (updatedErrors.size > 0) {
|
|
121
|
+
if (this.output) {
|
|
122
|
+
await this.output.render("Alert", {
|
|
123
|
+
variant: "error",
|
|
124
|
+
content: Array.from(updatedErrors.values()).join("\n")
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
errors = updatedErrors
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
msg.body = updatedBody
|
|
131
|
+
break
|
|
132
|
+
}
|
|
133
|
+
return msg.body
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Renders a message to the user interface.
|
|
138
|
+
* Must be implemented by subclasses.
|
|
139
|
+
*
|
|
140
|
+
* @param {UIMessage} message - Message to render.
|
|
141
|
+
* @emits rendered
|
|
142
|
+
* @throws {Error} If not implemented in subclass.
|
|
143
|
+
*/
|
|
144
|
+
render(message) {
|
|
145
|
+
throw new Error("render() must be implemented by subclass")
|
|
146
|
+
this.emit("rendered", message)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Generates a UIForm from a static Body schema.
|
|
152
|
+
*
|
|
153
|
+
* @param {Function} BodyClass - Class defining field schema.
|
|
154
|
+
* @param {Object} [options={}] - Generation options.
|
|
155
|
+
* @param {Object} [options.initialState={}] - Pre-filled form values.
|
|
156
|
+
* @param {Function} [options.t] - Optional translation function.
|
|
157
|
+
* @returns {UIForm} Form instance ready for input.
|
|
158
|
+
*/
|
|
159
|
+
export function generateForm(BodyClass, options = {}) {
|
|
160
|
+
const { initialState = {}, t = v => v } = options
|
|
161
|
+
const fields = []
|
|
162
|
+
|
|
163
|
+
for (const [name, schema] of Object.entries(BodyClass)) {
|
|
164
|
+
if (typeof schema !== "object" || schema === null || !name || !schema.help) continue
|
|
165
|
+
|
|
166
|
+
const label = t(schema.help) || name
|
|
167
|
+
const placeholder = t(schema.placeholder || schema.defaultValue || "")
|
|
168
|
+
const isRequired = !!schema.required
|
|
169
|
+
|
|
170
|
+
fields.push(
|
|
171
|
+
new FormInput({
|
|
172
|
+
name,
|
|
173
|
+
label,
|
|
174
|
+
type: schema.type || FormInput.TYPES.TEXT,
|
|
175
|
+
required: isRequired,
|
|
176
|
+
placeholder,
|
|
177
|
+
options: schema.options ? schema.options.map(o => String(o)) : [],
|
|
178
|
+
validation: schema.validate
|
|
179
|
+
? (value) => {
|
|
180
|
+
const res = schema.validate(value)
|
|
181
|
+
return res === true ? true : typeof res === "string" ? res : `Invalid ${name}`
|
|
182
|
+
}
|
|
183
|
+
: () => true,
|
|
184
|
+
}),
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return new UIForm({ fields })
|
|
189
|
+
}
|
package/src/core/index.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
// export default App
|
|
2
|
-
|
|
3
1
|
export { default as InputAdapter } from "./InputAdapter.js"
|
|
4
2
|
export { default as OutputAdapter } from "./OutputAdapter.js"
|
|
5
3
|
export { default as UIStream } from "./Stream.js"
|
|
6
4
|
|
|
7
|
-
export { default as
|
|
8
|
-
export { default as InputMessage } from "./Message/InputMessage.js"
|
|
9
|
-
export { default as OutputMessage } from "./Message/OutputMessage.js"
|
|
10
|
-
|
|
5
|
+
export { default as UiMessage } from "./Message/Message.js"
|
|
11
6
|
export { default as FormMessage } from "./Form/Message.js"
|
|
12
7
|
export { default as FormInput } from "./Form/Input.js"
|
|
13
8
|
export { default as UIForm } from "./Form/Form.js"
|
|
9
|
+
|
|
10
|
+
export { default as Error, CancelError } from "./Error/index.js"
|
|
11
|
+
|
|
12
|
+
export { default as UiAdapter } from "./UiAdapter.js"
|
package/src/index.js
CHANGED
|
@@ -26,9 +26,10 @@ export {
|
|
|
26
26
|
export { default as FormMessage } from "./core/Form/Message.js"
|
|
27
27
|
export { default as FormInput } from "./core/Form/Input.js"
|
|
28
28
|
export { default as InputAdapter } from "./core/InputAdapter.js"
|
|
29
|
-
export { default as InputMessage } from "./core/Message/InputMessage.js"
|
|
30
29
|
export { default as OutputAdapter } from "./core/OutputAdapter.js"
|
|
31
30
|
export { default as OutputMessage } from "./core/Message/OutputMessage.js"
|
|
32
|
-
export { default as
|
|
33
|
-
export { default as
|
|
34
|
-
export { default as
|
|
31
|
+
export { default as UiForm } from "./core/Form/Form.js"
|
|
32
|
+
export { default as UiMessage } from "./core/Message/Message.js"
|
|
33
|
+
export { default as UiStream } from "./core/Stream.js"
|
|
34
|
+
export { default as Error, CancelError } from "./core/Error/index.js"
|
|
35
|
+
export { default as UiAdapter } from "./core/UiAdapter.js"
|
|
@@ -9,12 +9,12 @@ export default class CoreApp {
|
|
|
9
9
|
* @param {object} props - CoreApp properties
|
|
10
10
|
* @param {string} [props.name="CoreApp"] - App name
|
|
11
11
|
* @param {object} [props.state={}] - Initial state object
|
|
12
|
-
* @param {
|
|
12
|
+
* @param {Message} [props.startCommand=new Message()] - Command line arguments to parse
|
|
13
13
|
*/
|
|
14
14
|
constructor(props?: {
|
|
15
15
|
name?: string | undefined;
|
|
16
16
|
state?: object;
|
|
17
|
-
|
|
17
|
+
startCommand?: Message | undefined;
|
|
18
18
|
});
|
|
19
19
|
/** @type {string} App name */
|
|
20
20
|
name: string;
|
|
@@ -22,8 +22,8 @@ export default class CoreApp {
|
|
|
22
22
|
commands: Map<string, CommandFn>;
|
|
23
23
|
/** @type {object} App state */
|
|
24
24
|
state: object;
|
|
25
|
-
/** @type {
|
|
26
|
-
startCommand:
|
|
25
|
+
/** @type {Message} Starting command parsed from argv */
|
|
26
|
+
startCommand: Message;
|
|
27
27
|
/**
|
|
28
28
|
* Sets app state.
|
|
29
29
|
* @param {string|object} state - State key or object with multiple keys
|
|
@@ -44,19 +44,19 @@ export default class CoreApp {
|
|
|
44
44
|
toString(): string;
|
|
45
45
|
/**
|
|
46
46
|
* Process a command message.
|
|
47
|
-
* @param {
|
|
47
|
+
* @param {Message} msg - Command to process
|
|
48
48
|
* @param {UI} ui - UI instance to use for rendering
|
|
49
49
|
* @returns {Promise<any>} Output of the command
|
|
50
50
|
* @throws {Error} If the command is not registered
|
|
51
51
|
*/
|
|
52
|
-
processCommand(
|
|
52
|
+
processCommand(msg: Message, ui: UI): Promise<any>;
|
|
53
53
|
/**
|
|
54
54
|
* Process an array of command messages sequentially.
|
|
55
|
-
* @param {
|
|
55
|
+
* @param {Message[]} Messages - Array of commands to process
|
|
56
56
|
* @param {UI} ui - UI instance to use for rendering
|
|
57
57
|
* @returns {Promise<any[]>} Array of command outputs
|
|
58
58
|
*/
|
|
59
|
-
processCommands(
|
|
59
|
+
processCommands(Messages: Message[], ui: UI): Promise<any[]>;
|
|
60
60
|
/**
|
|
61
61
|
* Select a command to run. Must be implemented by subclasses.
|
|
62
62
|
* @param {UI} ui - UI instance for interaction
|
|
@@ -66,5 +66,5 @@ export default class CoreApp {
|
|
|
66
66
|
selectCommand(ui: UI): Promise<string>;
|
|
67
67
|
}
|
|
68
68
|
export type CommandFn = Function;
|
|
69
|
-
import {
|
|
69
|
+
import { Message } from "@nan0web/co";
|
|
70
70
|
import UI from "./UI.js";
|
package/types/App/Core/UI.d.ts
CHANGED
|
@@ -11,27 +11,17 @@ declare class UI extends Widget {
|
|
|
11
11
|
* @param {CoreApp} app - The app to connect to this UI
|
|
12
12
|
* @param {View} [view] - View instance for rendering (default: new View())
|
|
13
13
|
*/
|
|
14
|
-
constructor(app: CoreApp, view?: View
|
|
14
|
+
constructor(app: CoreApp, view?: View);
|
|
15
15
|
/** @type {CoreApp} The app instance connected to this UI */
|
|
16
16
|
app: CoreApp;
|
|
17
17
|
/**
|
|
18
|
-
* Convert raw input to
|
|
18
|
+
* Convert raw input to Message array.
|
|
19
19
|
* Must be implemented by subclasses.
|
|
20
20
|
* @param {any} rawInput - Raw input to convert
|
|
21
|
-
* @returns {
|
|
21
|
+
* @returns {Message[]} Array of command messages
|
|
22
22
|
* @throws {Error} Always thrown as this method must be implemented by subclasses
|
|
23
23
|
*/
|
|
24
|
-
convertInput(rawInput: any):
|
|
25
|
-
/**
|
|
26
|
-
* Process input, run commands on app, and output results.
|
|
27
|
-
* Supports progress callback.
|
|
28
|
-
* @emits {start} Emitted when processing begins
|
|
29
|
-
* @emits {data} Emitted for each command being processed
|
|
30
|
-
* @emits {end} Emitted when all commands have been processed
|
|
31
|
-
* @param {any} rawInput - Raw input to process
|
|
32
|
-
* @returns {Promise<any[]>} Results of command processing
|
|
33
|
-
*/
|
|
34
|
-
process(rawInput: any): Promise<any[]>;
|
|
24
|
+
convertInput(rawInput: any): Message[];
|
|
35
25
|
/**
|
|
36
26
|
* Sets up event handlers for UI process events.
|
|
37
27
|
* @param {ComponentFn} UIProcess - Process view component
|
|
@@ -45,5 +35,5 @@ declare class UI extends Widget {
|
|
|
45
35
|
}
|
|
46
36
|
import Widget from "./Widget.js";
|
|
47
37
|
import CoreApp from "./CoreApp.js";
|
|
48
|
-
import {
|
|
38
|
+
import { Message } from "@nan0web/co";
|
|
49
39
|
import View from "../../View/View.js";
|
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
export default Widget;
|
|
2
|
-
export type ComponentFn = import("./UI.js").ComponentFn;
|
|
3
1
|
/** @typedef {import("./UI.js").ComponentFn} ComponentFn */
|
|
4
2
|
/**
|
|
5
3
|
* Abstract Widget class.
|
|
6
4
|
* Widget is a view with ability to input data in a specific format.
|
|
7
5
|
* Input and output data are typed classes.
|
|
8
6
|
*/
|
|
9
|
-
|
|
7
|
+
export default class Widget extends EventProcessor {
|
|
10
8
|
/**
|
|
11
9
|
* Creates a new Widget instance.
|
|
12
10
|
* @param {View} [view] - View instance (default: new View())
|
|
13
11
|
*/
|
|
14
|
-
constructor(view?: View
|
|
12
|
+
constructor(view?: View);
|
|
15
13
|
/** @type {View} The view associated with this widget */
|
|
16
14
|
view: View;
|
|
17
15
|
/**
|
|
18
16
|
* Ask user for input data of specific class.
|
|
19
|
-
* @param {
|
|
20
|
-
* @returns {Promise<
|
|
17
|
+
* @param {UiMessage} input - instance of UiMessage or similar
|
|
18
|
+
* @returns {Promise<UiMessage | null>} instance of UiMessage or null
|
|
21
19
|
*/
|
|
22
|
-
ask(input:
|
|
20
|
+
ask(input: UiMessage): Promise<UiMessage | null>;
|
|
23
21
|
/**
|
|
24
22
|
* @param {AsyncGenerator<StreamEntry>} stream
|
|
25
23
|
* @returns {Promise<void>}
|
|
@@ -34,7 +32,8 @@ declare class Widget extends EventProcessor {
|
|
|
34
32
|
*/
|
|
35
33
|
render(viewFnOrName: Function | string, outputData: object): any;
|
|
36
34
|
}
|
|
35
|
+
export type ComponentFn = import("./UI.js").ComponentFn;
|
|
37
36
|
import EventProcessor from "@nan0web/event/oop";
|
|
38
37
|
import View from "../../View/View.js";
|
|
39
|
-
import
|
|
38
|
+
import { UiMessage } from "../../core/index.js";
|
|
40
39
|
import { StreamEntry } from "@nan0web/db";
|
|
@@ -1,30 +1,16 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* @param {Partial<UserAppCommandOptions>} [props.opts={}] - User-specific options
|
|
17
|
-
*/
|
|
18
|
-
constructor(props?: {
|
|
19
|
-
args?: string[] | undefined;
|
|
20
|
-
opts?: Partial<UserAppCommandOptions> | undefined;
|
|
21
|
-
});
|
|
22
|
-
/**
|
|
23
|
-
* @param {Partial<UserAppCommandOptions>} value
|
|
24
|
-
*/
|
|
25
|
-
set opts(arg: UserAppCommandOptions);
|
|
26
|
-
/** @returns {UserAppCommandOptions} */
|
|
27
|
-
get opts(): UserAppCommandOptions;
|
|
1
|
+
export class DepsCommand extends UIMessage {
|
|
2
|
+
static Body: typeof DepsCommandParams;
|
|
3
|
+
constructor(input?: {});
|
|
4
|
+
/** @type {DepsCommandParams} */
|
|
5
|
+
body: DepsCommandParams;
|
|
6
|
+
}
|
|
7
|
+
export default DepsCommand;
|
|
8
|
+
import UIMessage from "../../../core/Message/Message.js";
|
|
9
|
+
declare class DepsCommandParams {
|
|
10
|
+
static fix: {
|
|
11
|
+
help: string;
|
|
12
|
+
defaultValue: boolean;
|
|
13
|
+
};
|
|
14
|
+
constructor(input?: {});
|
|
15
|
+
fix: boolean;
|
|
28
16
|
}
|
|
29
|
-
import { CommandMessage } from "../../Command/index.js";
|
|
30
|
-
import UserAppCommandOptions from "./Options.js";
|