@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.
Files changed (58) hide show
  1. package/README.md +19 -54
  2. package/package.json +21 -17
  3. package/src/App/Core/CoreApp.js +14 -19
  4. package/src/App/Core/UI.js +4 -50
  5. package/src/App/Core/Widget.js +4 -6
  6. package/src/App/User/Command/Message.js +23 -37
  7. package/src/App/User/Command/index.js +3 -8
  8. package/src/App/User/UserApp.js +32 -27
  9. package/src/App/User/UserUI.js +4 -4
  10. package/src/App/User/index.js +0 -6
  11. package/src/App/index.js +0 -3
  12. package/src/README.md.js +33 -57
  13. package/src/StdIn.js +12 -15
  14. package/src/View/View.js +5 -5
  15. package/src/core/Error/index.js +9 -0
  16. package/src/core/Form/Form.js +43 -23
  17. package/src/core/Form/Input.js +16 -7
  18. package/src/core/Form/Message.js +6 -8
  19. package/src/core/InputAdapter.js +6 -2
  20. package/src/core/Message/Message.js +109 -19
  21. package/src/core/Message/OutputMessage.js +8 -8
  22. package/src/core/Message/index.js +3 -4
  23. package/src/core/Stream.js +10 -10
  24. package/src/core/UiAdapter.js +189 -0
  25. package/src/core/index.js +5 -6
  26. package/src/index.js +5 -4
  27. package/types/App/Core/CoreApp.d.ts +9 -9
  28. package/types/App/Core/UI.d.ts +5 -15
  29. package/types/App/Core/Widget.d.ts +7 -8
  30. package/types/App/User/Command/Message.d.ts +15 -29
  31. package/types/App/User/Command/Options.d.ts +9 -2
  32. package/types/App/User/Command/index.d.ts +3 -7
  33. package/types/App/User/UserApp.d.ts +19 -9
  34. package/types/App/User/UserUI.d.ts +0 -9
  35. package/types/App/User/index.d.ts +0 -4
  36. package/types/App/index.d.ts +1 -3
  37. package/types/Frame/Frame.d.ts +5 -5
  38. package/types/StdIn.d.ts +13 -13
  39. package/types/View/View.d.ts +9 -9
  40. package/types/core/Error/index.d.ts +6 -0
  41. package/types/core/Form/Form.d.ts +14 -11
  42. package/types/core/Form/Input.d.ts +20 -7
  43. package/types/core/Form/Message.d.ts +5 -4
  44. package/types/core/InputAdapter.d.ts +2 -0
  45. package/types/core/Intent.d.ts +91 -0
  46. package/types/core/Message/InputMessage.d.ts +5 -5
  47. package/types/core/Message/Message.d.ts +58 -15
  48. package/types/core/Message/OutputMessage.d.ts +4 -4
  49. package/types/core/Message/index.d.ts +3 -4
  50. package/types/core/Stream.d.ts +5 -4
  51. package/types/core/StreamEntry.d.ts +1 -1
  52. package/types/core/UiAdapter.d.ts +104 -0
  53. package/types/core/index.d.ts +3 -3
  54. package/types/index.d.ts +5 -4
  55. package/src/App/Command/Options.js +0 -78
  56. package/src/App/Command/index.js +0 -9
  57. package/src/App/User/Command/Options.js +0 -48
  58. package/src/core/Message/InputMessage.js +0 -119
package/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # @nan0web/ui
2
2
 
3
- |[Status](https://github.com/nan0web/monorepo/blob/main/system.md#написання-сценаріїв)|Documentation|Test coverage|Features|Npm version|
4
- |---|---|---|---|---|
5
- |🟢 `96.8%` |🧪 [English 🏴󠁧󠁢󠁥󠁮󠁧󠁿](https://github.com/nan0web/ui/blob/main/README.md)<br />[Українською 🇺🇦](https://github.com/nan0web/ui/blob/main/docs/uk/README.md) |🟡 `81.1%` |✅ d.ts 📜 system.md 🕹️ playground |1.0.1 |
3
+ <!-- %PACKAGE_STATUS% -->
6
4
 
7
5
  A lightweight, agnostic UI framework designed with the **nan0web philosophy**
8
6
  — one application logic, many UI implementations.
@@ -43,8 +41,7 @@ yarn add @nan0web/ui
43
41
 
44
42
  UI communication is built around messages:
45
43
 
46
- - **`UIMessage`** – abstract message base class
47
- - **`InputMessage`** – user input message (value, options)
44
+ - **`UiMessage`** – abstract message base class
48
45
  - **`OutputMessage`** – system output (content, error, priority)
49
46
 
50
47
  Messages are simple, serializable data containers. They help build
@@ -53,15 +50,14 @@ decoupled communication systems between UI components.
53
50
  How to create input and output messages?
54
51
  ```js
55
52
  import { InputMessage, OutputMessage } from '@nan0web/ui'
56
-
57
- const input = InputMessage.from({ value: 'Hello User' })
53
+ const input = UiMessage.from({ body: 'Hello User' })
58
54
  const output = OutputMessage.from({ content: ['Welcome to @nan0web/ui'] })
59
- console.info(input.value) // ← Hello User
60
- console.info(output.content[0]) // ← Welcome to @nan0web/ui
55
+ console.info(input) // ← Message { body: "Hello User", head: {}, id: "....", type: "" }
56
+ console.info(String(output)) // ← Welcome to @nan0web/ui
61
57
  ```
62
58
  ### Forms
63
59
 
64
- `UIForm` supports field definitions, data management, and schema validation.
60
+ `UiForm` supports field definitions, data management, and schema validation.
65
61
  Every form includes a title, fields, and current state.
66
62
 
67
63
  Field types include:
@@ -73,11 +69,10 @@ Field types include:
73
69
  - `checkbox`
74
70
  - `textarea`
75
71
 
76
- How to define and validate a UIForm?
72
+ How to define and validate a UiForm?
77
73
  ```js
78
- import { UIForm } from '@nan0web/ui'
79
-
80
- const form = new UIForm({
74
+ import { UiForm } from '@nan0web/ui'
75
+ const form = new UiForm({
81
76
  title: "Contact Form",
82
77
  fields: [
83
78
  FormInput.from({ name: "email", label: "Email Address", type: "email", required: true }),
@@ -88,11 +83,9 @@ const form = new UIForm({
88
83
  message: "Hello!"
89
84
  }
90
85
  })
91
-
92
- const result = form.validate()
93
- console.info(result.isValid) // ← false
94
- console.info(result.errors.email) // ← Invalid email format
95
-
86
+ const errors = form.validate()
87
+ console.info(errors.size) // 1
88
+ console.info(errors.get("email")) // ← Invalid email format
96
89
  ```
97
90
  ### Components
98
91
 
@@ -104,7 +97,6 @@ Components render data as frame-ready output.
104
97
  How to render the Welcome component?
105
98
  ```js
106
99
  import { Welcome } from '@nan0web/ui'
107
-
108
100
  const frame = Welcome({ user: { name: "Alice" } })
109
101
  const firstLine = frame[0].join("")
110
102
  console.info(firstLine) // ← Welcome Alice!
@@ -122,7 +114,6 @@ Every view has:
122
114
  How to render frame with View?
123
115
  ```js
124
116
  import { View } from '@nan0web/ui'
125
-
126
117
  const view = new View()
127
118
  view.render(1)(["Hello, world"])
128
119
  console.info(String(view.frame)) // ← "\rHello, world"
@@ -141,38 +132,15 @@ Render methods:
141
132
  How to create a Frame with fixed size?
142
133
  ```js
143
134
  import { Frame } from '@nan0web/ui'
144
-
145
135
  const frame = new Frame({
146
136
  value: [["Frame content"]],
147
137
  width: 20,
148
138
  height: 5,
149
139
  renderMethod: Frame.RenderMethod.APPEND,
150
140
  })
151
-
152
141
  const rendered = frame.render()
153
142
  console.info(rendered.includes("Frame content")) // ← true
154
143
  ```
155
- ### App Architecture
156
-
157
- `App` provides the main application logic.
158
-
159
- - Core – minimal UI layer
160
- - User – user-specific UI commands
161
-
162
- Each app registers commands and binds them to UI actions.
163
-
164
- How to create a basic user app that greets?
165
- ```js
166
- import { App, View } from '@nan0web/ui'
167
-
168
- const app = new App.User.App({ name: "GreetApp" })
169
- const view = new View()
170
- view.register("Welcome", Welcome)
171
-
172
- const cmd = App.Command.Message.parse("welcome --user Bob")
173
- const result = await app.processCommand(cmd, new App.User.UI(app, view))
174
- console.info(String(result)) // ← Welcome Bob!
175
- ```
176
144
  ### Models
177
145
 
178
146
  UI models are plain data objects managed by `Model` classes.
@@ -182,7 +150,6 @@ UI models are plain data objects managed by `Model` classes.
182
150
  How to use a User model?
183
151
  ```js
184
152
  import { Model } from '@nan0web/ui'
185
-
186
153
  const user = new Model.User({ name: "Charlie", email: "charlie@example.com" })
187
154
  console.info(user.name) // ← Charlie
188
155
  console.info(user.email) // ← charlie@example.com
@@ -196,20 +163,18 @@ with minimal setup.
196
163
 
197
164
  How to test UI components with assertions?
198
165
  ```js
199
- import { Welcome, InputMessage } from '@nan0web/ui'
200
-
166
+ import { Welcome } from '@nan0web/ui'
201
167
  const output = Welcome({ user: { name: "Test" } })
202
- const input = InputMessage.from({ value: "test" })
203
- console.log(output[0].join("")) // ← Welcome Test!
168
+ console.info(output) // Welcome Test!
204
169
  ```
205
170
  ## Playground Demos
206
171
 
207
172
  The library includes rich playground demos:
208
173
 
209
- - [Registration Form](./playground/registration.form.js)
210
- - [Currency Exchange](./playground/currency.exchange.js)
211
- - [Mobile Top-up](./playground/topup.telephone.js)
212
- - [Language Selector](./playground/language.form.js)
174
+ - [Registration Form](./play/registration.form.js)
175
+ - [Currency Exchange](./play/currency.exchange.js)
176
+ - [Mobile Top-up](./play/topup.telephone.js)
177
+ - [Language Selector](./play/language.form.js)
213
178
 
214
179
  Run to explore live functionality:
215
180
 
@@ -219,7 +184,7 @@ How to run the playground?
219
184
  git clone https://github.com/nan0web/ui.git
220
185
  cd ui
221
186
  npm install
222
- npm run playground
187
+ npm run play
223
188
  ```
224
189
 
225
190
  ## API Documentation
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nan0web/ui",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "NaN•Web UI. One application logic (algorithm) and many UI.",
5
5
  "main": "src/index.js",
6
6
  "types": "types/index.d.ts",
@@ -12,13 +12,16 @@
12
12
  ],
13
13
  "scripts": {
14
14
  "build": "tsc",
15
- "playground": "node playground/main.js",
16
- "test": "node --test --test-timeout=3333 \"src/**/*.test.js\" | nan0test parse --fail",
15
+ "play": "node play/main.js",
16
+ "test": "node --test --test-timeout=3333 \"src/**/*.test.js\"",
17
+ "test:nan0test": "node --test --test-timeout=3333 \"src/**/*.test.js\" | nan0test parse --fail",
17
18
  "test:coverage": "node --experimental-test-coverage --test-coverage-include=\"src/**/*.js\" --test-coverage-exclude=\"src/**/*.test.js\" --test \"src/**/*.test.js\"",
18
19
  "test:coverage:collect": "nan0test coverage",
19
20
  "test:docs": "node --test src/README.md.js",
20
21
  "test:release": "node --test \"releases/**/*.test.js\"",
21
22
  "test:status": "nan0test status --hide-name",
23
+ "test:play": "node --test --test-timeout=3333 \"play/**/*.test.js\"",
24
+ "test:all": "npm run test && npm run test:docs && npm run test:play && npm run build",
22
25
  "precommit": "npm test",
23
26
  "prepush": "npm test",
24
27
  "prepare": "husky",
@@ -35,6 +38,10 @@
35
38
  "import": "./src/Component/index.js",
36
39
  "types": "./types/Component/index.d.ts"
37
40
  },
41
+ "./core": {
42
+ "import": "./src/core/index.js",
43
+ "types": "./types/core/index.d.ts"
44
+ },
38
45
  "./cli-app": "./apps/cli/src/index.js",
39
46
  "./mobile-app": "./apps/mobile/src/index.js",
40
47
  "./web-app": "./apps/web/src/App.jsx"
@@ -47,23 +54,20 @@
47
54
  "license": "ISC",
48
55
  "packageManager": "pnpm@10.11.0",
49
56
  "devDependencies": {
50
- "@nan0web/co": "workspace:*",
51
- "@nan0web/event": "workspace:*",
52
- "@nan0web/i18n": "workspace:*",
53
- "@nan0web/release": "workspace:*",
54
- "@nan0web/test": "workspace:*",
55
- "@nan0web/types": "workspace:*",
56
- "@nan0web/ui-cli": "workspace:*",
57
+ "@nan0web/event": "^1.0.0",
58
+ "@nan0web/i18n": "^1.0.1",
59
+ "@nan0web/release": "1.0.1",
60
+ "@nan0web/test": "1.1.0",
61
+ "@nan0web/ui-cli": "1.0.2",
57
62
  "@vitest/coverage-v8": "^3.2.4",
58
- "vitest": "^3.2.4",
59
- "husky": "^9.1.7"
63
+ "husky": "^9.1.7",
64
+ "vitest": "^3.2.4"
60
65
  },
61
66
  "dependencies": {
67
+ "@nan0web/co": "^2.0.0",
68
+ "@nan0web/event": "^1.0.0",
69
+ "@nan0web/types": "^1.2.0",
62
70
  "string-width": "^7.2.0"
63
71
  },
64
- "peerDependencies": {
65
- "@nan0web/co": "^1.0.0",
66
- "@nan0web/event": "^1.0.0",
67
- "@nan0web/types": "^1.0.0"
68
- }
72
+ "peerDependencies": {}
69
73
  }
@@ -1,5 +1,5 @@
1
+ import { Message } from "@nan0web/co"
1
2
  import { typeOf } from "@nan0web/types"
2
- import { CommandMessage } from "../Command/index.js"
3
3
  import UI from "./UI.js"
4
4
 
5
5
  /** @typedef {Function} CommandFn */
@@ -18,7 +18,7 @@ export default class CoreApp {
18
18
  /** @type {object} App state */
19
19
  state
20
20
 
21
- /** @type {CommandMessage} Starting command parsed from argv */
21
+ /** @type {Message} Starting command parsed from argv */
22
22
  startCommand
23
23
 
24
24
  /**
@@ -26,18 +26,19 @@ export default class CoreApp {
26
26
  * @param {object} props - CoreApp properties
27
27
  * @param {string} [props.name="CoreApp"] - App name
28
28
  * @param {object} [props.state={}] - Initial state object
29
- * @param {string[]} [props.argv=[]] - Command line arguments to parse
29
+ * @param {Message} [props.startCommand=new Message()] - Command line arguments to parse
30
30
  */
31
31
  constructor(props = {}) {
32
32
  const {
33
33
  name = "CoreApp",
34
34
  state = {},
35
- argv = [],
35
+ startCommand = new Message(),
36
36
  } = props
37
37
  this.name = String(name)
38
38
  this.state = state
39
39
  this.commands = new Map()
40
- this.startCommand = CommandMessage.parse(argv)
40
+ // @deprecated @todo fix the argv by moving to ui-cli.
41
+ this.startCommand = Message.from(startCommand ?? {})
41
42
  }
42
43
 
43
44
  /**
@@ -78,39 +79,33 @@ export default class CoreApp {
78
79
 
79
80
  /**
80
81
  * Process a command message.
81
- * @param {CommandMessage} commandMessage - Command to process
82
+ * @param {Message} msg - Command to process
82
83
  * @param {UI} ui - UI instance to use for rendering
83
84
  * @returns {Promise<any>} Output of the command
84
85
  * @throws {Error} If the command is not registered
85
86
  */
86
- async processCommand(commandMessage, ui) {
87
- const cmd = commandMessage.args[0]
88
- const handler = this.commands.get(cmd)
87
+ async processCommand(msg, ui) {
88
+ const handler = this.commands.get(msg.constructor.name)
89
89
  if (!handler) {
90
90
  throw new Error([
91
91
  "Unknown command", ": ",
92
- cmd, "\n",
92
+ msg.constructor.name, "\n",
93
93
  "Available commands", ": ",
94
94
  [...this.commands.keys()].join(", "),
95
95
  ].join(""))
96
96
  }
97
- const Class = /** @type {typeof CommandMessage} */ (commandMessage.constructor)
98
- const command = Class.from({
99
- args: commandMessage.args.slice(1),
100
- opts: commandMessage.opts,
101
- })
102
- return await handler.apply(this, [command, ui])
97
+ return await handler.apply(this, [msg, ui])
103
98
  }
104
99
 
105
100
  /**
106
101
  * Process an array of command messages sequentially.
107
- * @param {CommandMessage[]} commandMessages - Array of commands to process
102
+ * @param {Message[]} Messages - Array of commands to process
108
103
  * @param {UI} ui - UI instance to use for rendering
109
104
  * @returns {Promise<any[]>} Array of command outputs
110
105
  */
111
- async processCommands(commandMessages, ui) {
106
+ async processCommands(Messages, ui) {
112
107
  const results = []
113
- for (const cmdMsg of commandMessages) {
108
+ for (const cmdMsg of Messages) {
114
109
  const result = await this.processCommand(cmdMsg, ui)
115
110
  results.push(result)
116
111
  }
@@ -1,8 +1,8 @@
1
+ import { Message } from "@nan0web/co"
2
+ import { notEmpty } from "@nan0web/types"
1
3
  import View from "../../View/View.js"
2
4
  import CoreApp from "./CoreApp.js"
3
5
  import Widget from "./Widget.js"
4
- import { notEmpty } from "@nan0web/types"
5
- import { CommandMessage } from "../Command/index.js"
6
6
 
7
7
  /** @typedef {import("../../View/View.js").ComponentFn} ComponentFn */
8
8
 
@@ -25,62 +25,16 @@ class UI extends Widget {
25
25
  }
26
26
 
27
27
  /**
28
- * Convert raw input to CommandMessage array.
28
+ * Convert raw input to Message array.
29
29
  * Must be implemented by subclasses.
30
30
  * @param {any} rawInput - Raw input to convert
31
- * @returns {CommandMessage[]} Array of command messages
31
+ * @returns {Message[]} Array of command messages
32
32
  * @throws {Error} Always thrown as this method must be implemented by subclasses
33
33
  */
34
34
  convertInput(rawInput) {
35
35
  throw new Error("convertInput must be implemented by subclass")
36
36
  }
37
37
 
38
- /**
39
- * Process input, run commands on app, and output results.
40
- * Supports progress callback.
41
- * @emits {start} Emitted when processing begins
42
- * @emits {data} Emitted for each command being processed
43
- * @emits {end} Emitted when all commands have been processed
44
- * @param {any} rawInput - Raw input to process
45
- * @returns {Promise<any[]>} Results of command processing
46
- */
47
- async process(rawInput) {
48
- const commands = this.convertInput(rawInput).filter(notEmpty)
49
- const results = []
50
- let count = commands.length
51
-
52
- const proc = this.view.get("UIProcess")
53
- if (proc) {
54
- this.show(proc)
55
- }
56
-
57
- if (0 === count && this.app.selectCommand) {
58
- const answer = await this.app.selectCommand(this)
59
- if (answer) {
60
- const [input] = this.convertInput(rawInput)
61
- const cmd = new CommandMessage({
62
- argv: [answer],
63
- opts: input?.opts ?? {},
64
- })
65
- commands.push(cmd)
66
- ++count
67
- } else {
68
- this.emit("end", { commands, results })
69
- return results
70
- }
71
- }
72
- this.emit("start", { commands })
73
- for (let i = 0; i < count; i++) {
74
- const command = commands[i]
75
- this.emit("data", { i, count, command })
76
- const result = await this.app.processCommand(command, this)
77
- results.push(result)
78
- }
79
- this.emit("end", { commands, results })
80
- // this.output(results)
81
- return results
82
- }
83
-
84
38
  /**
85
39
  * Sets up event handlers for UI process events.
86
40
  * @param {ComponentFn} UIProcess - Process view component
@@ -1,7 +1,7 @@
1
1
  import EventProcessor from "@nan0web/event/oop"
2
2
  import View from "../../View/View.js"
3
- import InputMessage from "../../core/Message/InputMessage.js"
4
3
  import { StreamEntry } from "@nan0web/db"
4
+ import { UiMessage } from "../../core/index.js"
5
5
 
6
6
  /** @typedef {import("./UI.js").ComponentFn} ComponentFn */
7
7
 
@@ -10,7 +10,7 @@ import { StreamEntry } from "@nan0web/db"
10
10
  * Widget is a view with ability to input data in a specific format.
11
11
  * Input and output data are typed classes.
12
12
  */
13
- class Widget extends EventProcessor {
13
+ export default class Widget extends EventProcessor {
14
14
  /** @type {View} The view associated with this widget */
15
15
  view
16
16
 
@@ -25,8 +25,8 @@ class Widget extends EventProcessor {
25
25
 
26
26
  /**
27
27
  * Ask user for input data of specific class.
28
- * @param {InputMessage} input - instance of InputMessage or similar
29
- * @returns {Promise<InputMessage | null>} instance of InputMessage or null
28
+ * @param {UiMessage} input - instance of UiMessage or similar
29
+ * @returns {Promise<UiMessage | null>} instance of UiMessage or null
30
30
  */
31
31
  async ask(input) {
32
32
  return await this.view.ask(input)
@@ -63,5 +63,3 @@ class Widget extends EventProcessor {
63
63
  return this.view.render(viewFn)(outputData)
64
64
  }
65
65
  }
66
-
67
- export default Widget
@@ -1,44 +1,30 @@
1
- import { CommandMessage } from "../../Command/index.js"
2
- import UserAppCommandOptions from "./Options.js"
1
+ import Message from "@nan0web/co"
2
+ import UIMessage from "../../../core/Message/Message.js"
3
3
 
4
- /**
5
- * Extends Command.Message to include user-specific command options.
6
- */
7
- class UserAppCommandMessage extends CommandMessage {
8
- /**
9
- * Creates a new UserAppCommandMessage instance.
10
- * @param {object} props - Command message properties
11
- * @param {string[]} [props.args=[]] - Command arguments
12
- * @param {Partial<UserAppCommandOptions>} [props.opts={}] - User-specific options
13
- */
14
- constructor(props = {}) {
15
- super(props)
4
+ class DepsCommandParams {
5
+ fix = false
6
+ static fix = {
7
+ help: "Fix dependencies",
8
+ defaultValue: false
16
9
  }
17
-
18
- /** @returns {UserAppCommandOptions} */
19
- get opts() {
20
- return UserAppCommandOptions.from(super.opts)
21
- }
22
-
23
- /**
24
- * @param {Partial<UserAppCommandOptions>} value
25
- */
26
- set opts(value) {
27
- super.opts = UserAppCommandOptions.from(value)
10
+ constructor(input = {}) {
11
+ const {
12
+ fix = this.fix
13
+ } = input
28
14
  }
15
+ }
29
16
 
30
- /**
31
- * Parses an array of strings into a UserAppCommandMessage.
32
- * @param {string[] | string} value - Arguments to parse
33
- * @returns {UserAppCommandMessage} Parsed command message
34
- */
35
- static parse(value = []) {
36
- if ("string" === typeof value) {
37
- value = value.split(" ")
38
- }
39
- const result = super.parse(value)
40
- return new this(result)
17
+ export class DepsCommand extends UIMessage {
18
+ static Body = DepsCommandParams
19
+ /** @type {DepsCommandParams} */
20
+ body
21
+ constructor(input = {}) {
22
+ const {
23
+ body = new DepsCommandParams()
24
+ } = UIMessage.parseBody(input, DepsCommandParams)
25
+ super(input)
26
+ this.body = body
41
27
  }
42
28
  }
43
29
 
44
- export default UserAppCommandMessage
30
+ export default DepsCommand
@@ -1,11 +1,6 @@
1
- import Command from "../../Command/index.js"
2
1
  import CommandMessage from "./Message.js"
3
- import CommandOptions from "./Options.js"
2
+ import DepsCommand from "./Message.js"
4
3
 
5
- export { CommandMessage, CommandOptions }
4
+ export { CommandMessage, DepsCommand }
6
5
 
7
- export default {
8
- ...Command,
9
- Message: CommandMessage,
10
- Options: CommandOptions,
11
- }
6
+ export default CommandMessage
@@ -1,49 +1,56 @@
1
+ import { Message } from "@nan0web/co"
1
2
  import { notEmpty } from "@nan0web/types"
2
3
  import CoreApp from "../Core/CoreApp.js"
3
- import Command, { CommandMessage } from "./Command/index.js"
4
4
  import User from "../../Model/User/User.js"
5
5
  import UserUI from "./UserUI.js"
6
- import InputMessage from "../../core/Message/InputMessage.js"
6
+ import UserAppCommandMessage from "./Command/Message.js"
7
+ import DepsCommand from "./Command/Message.js"
8
+ import UIStream from "../../core/Stream.js"
9
+ import { UiMessage } from "../../core/index.js"
7
10
 
8
11
  /**
9
12
  * UserApp requires user name and shows Welcome view.
10
13
  * If user.name is provided in command input, ignores user input.
11
14
  * User can change user data to see another Welcome view.
12
15
  */
13
- class UserApp extends CoreApp {
14
- /** @type {CommandMessage} Starting command parsed from argv */
15
- startCommand
16
-
17
- /** @type {object} App state */
18
- state
19
-
16
+ export default class UserApp extends CoreApp {
20
17
  /**
21
18
  * Creates a new UserApp instance.
22
- * @param {object} props - UserApp properties
23
- * @param {string} [props.name="UserApp"] - App name
24
- * @param {object} [props.state={}] - Initial state object
25
- * @param {string[]} [props.argv=[]] - Command line arguments to parse
19
+ * @param {Partial<CoreApp>} [props={}] - UserApp properties
26
20
  */
27
21
  constructor(props = {}) {
28
22
  super(props)
29
- const {
30
- argv = [],
31
- state = {},
32
- } = props
33
- this.state = state
34
- this.startCommand = CommandMessage.parse(argv)
35
23
  this.registerCommand("setUser", this.setUser.bind(this))
36
24
  this.registerCommand("welcome", this.welcome.bind(this))
25
+ this.registerCommand("deps", this.handleDeps.bind(this)) // Register new command
26
+ }
27
+
28
+ /**
29
+ * Handle deps command with async generator for stream processing.
30
+ * @param {DepsCommand} cmd - Command message with deps parameters
31
+ * @param {UserUI} ui - UI instance
32
+ * @returns {Promise<Object>} Command output
33
+ */
34
+ async handleDeps(cmd, ui) {
35
+ // Example: Use async generator to stream deps processing
36
+ const processorFn = async () => new UIStream.StreamEntry({ value: { message: `Deps command executed with fix: ${cmd.body.fix}` }, done: true })
37
+ const generatorFn = UIStream.createProcessor(new AbortController().signal, processorFn)
38
+ await UIStream.process(new AbortController().signal, generatorFn,
39
+ (progress, item) => ui.output && ui.output(item.value), // Fix to output the value
40
+ (error) => ui.output && ui.output({ error }), // Assume ui has output method
41
+ (item) => ui.output && ui.output(item.value) // Fix complete callback
42
+ )
43
+ return { completed: true }
37
44
  }
38
45
 
39
46
  /**
40
47
  * Set user data from params.
41
- * @param {CommandMessage} cmd - Command message with user data
48
+ * @param {UserAppCommandMessage} cmd - Command message with user data
42
49
  * @param {UserUI} ui - UI instance
43
50
  * @returns {Promise<{ message: string }>} Welcome message
44
51
  */
45
52
  async setUser(cmd, ui) {
46
- this.state.user = User.from(cmd.opts.user)
53
+ this.state.user = User.from(cmd.body.user) // cmd is UserAppCommandMessage, has user
47
54
  const frame = await this.welcome(cmd, ui)
48
55
  return {
49
56
  message: String(frame)
@@ -52,22 +59,20 @@ class UserApp extends CoreApp {
52
59
 
53
60
  /**
54
61
  * Show welcome message for current user.
55
- * @param {CommandMessage} cmd - Command message
62
+ * @param {UserAppCommandMessage} cmd - Command message
56
63
  * @param {UserUI} ui - UI instance
57
64
  * @returns {Promise<string[][]>} Welcome view output
58
65
  */
59
66
  async welcome(cmd, ui) {
60
- if (cmd.opts.user) {
61
- const user = User.from(cmd.opts.user)
67
+ if (cmd.body.user) { // cmd is UserAppCommandMessage, has user
68
+ const user = User.from(cmd.body.user)
62
69
  return ui.render("Welcome", { user })
63
70
  }
64
71
  if (notEmpty(this.user)) {
65
72
  return ui.render("Welcome", { user: this.user })
66
73
  }
67
- const answer = await ui.ask(InputMessage.from("What is your name?"))
74
+ const answer = await ui.ask(UiMessage.from("What is your name?"))
68
75
  this.user = User.from(answer?.value)
69
76
  return ui.render("Welcome", { user: this.user })
70
77
  }
71
78
  }
72
-
73
- export default UserApp
@@ -1,5 +1,5 @@
1
+ import { Message } from "@nan0web/co"
1
2
  import App from "../Core/index.js"
2
- import { CommandMessage } from "./Command/index.js"
3
3
 
4
4
  /**
5
5
  * UserUI connects UserApp and View.
@@ -8,13 +8,13 @@ import { CommandMessage } from "./Command/index.js"
8
8
  */
9
9
  export default class UserUI extends App.UI {
10
10
  /**
11
- * Convert raw input to CommandMessage array.
11
+ * Convert raw input to Message array.
12
12
  * If user.name provided in rawInput, use it directly.
13
13
  * Otherwise ask user for name.
14
14
  * @param {any} rawInput - Raw input to convert
15
- * @returns {CommandMessage[]} Array of command messages
15
+ * @returns {Message[]} Array of command messages
16
16
  */
17
17
  convertInput(rawInput) {
18
- return [CommandMessage.parse(rawInput)]
18
+ return [new Message({ body: rawInput })]
19
19
  }
20
20
  }
@@ -1,15 +1,9 @@
1
1
  import UserApp from "./UserApp.js"
2
2
  import UserUI from "./UserUI.js"
3
- import UserCommand from "./Command/index.js"
4
- import Command from "../Command/index.js"
5
3
 
6
4
  export { UserApp, UserUI }
7
5
 
8
6
  export default {
9
7
  App: UserApp,
10
8
  UI: UserUI,
11
- Command: {
12
- ...Command,
13
- ...UserCommand,
14
- },
15
9
  }