@nan0web/ui-cli 2.0.0 → 2.1.0

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 CHANGED
@@ -25,6 +25,49 @@ npm install @nan0web/ui-cli
25
25
 
26
26
  How to install the package?
27
27
 
28
+ ## nan0cli — Universal CLI Runner
29
+
30
+ The `nan0cli` binary provides a universal entry point for any nan0web application.
31
+ It reads the app's `package.json`, resolves the CLI entry point, and runs commands.
32
+
33
+ ### App Contract
34
+
35
+ Your app must export Messages from its entry point:
36
+
37
+ ```js
38
+ // E1: Messages Array (recommended)
39
+ export default [Serve, Dump]
40
+
41
+ // E2: Single Message class (auto-wrapped to array)
42
+ export default class MyApp { }
43
+ ```
44
+
45
+ ### Entry Point Resolution
46
+
47
+ `nan0cli` looks for the entry point in this order:
48
+ 1. `nan0web.cli.entry` field in `package.json`
49
+ 2. `src/cli.js` (convention)
50
+ 3. `src/messages/index.js` (legacy)
51
+
52
+ ### Configuration
53
+
54
+ ```json
55
+ {
56
+ "nan0web": {
57
+ "cli": { "entry": "src/cli.js" }
58
+ }
59
+ }
60
+ ```
61
+
62
+ nan0cli binary is registered
63
+
64
+ ### Error Handling
65
+
66
+ When no entry point is found, `nan0cli` displays a styled `Alert` error and exits with code 1.
67
+ All errors are displayed via `Logger` + `Alert` components — never raw `console.log`.
68
+
69
+ nan0cli is included in package files
70
+
28
71
  ## Usage (V2 Architecture)
29
72
 
30
73
  Starting from v2.0, we recommend using the `render()` function with Composable Components.
package/bin/cli.js ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { CLI } from '../src/index.js'
4
+
5
+ /*
6
+ The CLI class likely expects arguments or a run method.
7
+ I need to check src/index.js to see how to use CLI class.
8
+ */
9
+
10
+ const cli = new CLI()
11
+ // cli.run() ?
package/bin/nan0cli.js ADDED
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Universal NaN•Web CLI Runner
5
+ *
6
+ * Takes configuration from current working directory's package.json
7
+ * and executes command using @nan0web/ui-cli core logic.
8
+ *
9
+ * App contract:
10
+ * E1: export default [Serve, Dump] — Messages Array
11
+ * E2: export default class MyApp { } — Single Message class (auto-wrapped)
12
+ */
13
+
14
+ import { pathToFileURL } from 'node:url'
15
+
16
+ import DBFS from '@nan0web/db-fs'
17
+ import Logger from '@nan0web/log'
18
+
19
+ import { CLI, Alert, render } from '../src/index.js'
20
+
21
+ const console = new Logger({ level: Logger.detectLevel(process.argv) })
22
+ const fs = new DBFS()
23
+
24
+ /**
25
+ * Display error via Alert component + Logger
26
+ * @param {string} title
27
+ * @param {string} text
28
+ */
29
+ const showError = (title, text) => {
30
+ try {
31
+ console.error(render(new Alert({ type: 'error', title, description: text })))
32
+ } catch {
33
+ console.error(`[CRITICAL] ${title}: ${text}`)
34
+ }
35
+ }
36
+
37
+ ;(async () => {
38
+ try {
39
+ // 1. Load package.json via DBFS (A4)
40
+ const pkg = await fs.loadDocument('package.json', {})
41
+ const entry = pkg?.nan0web?.cli?.entry
42
+
43
+ // 2. Resolve entry point via statDocument (B3)
44
+ const candidates = [entry, 'src/cli.js', 'src/messages/index.js'].filter(Boolean)
45
+ let appPath = null
46
+
47
+ for (const candidate of candidates) {
48
+ const stat = await fs.statDocument(candidate)
49
+ if (!stat.error) {
50
+ appPath = fs.location(candidate)
51
+ break
52
+ }
53
+ }
54
+
55
+ if (!appPath) {
56
+ showError(
57
+ 'Config Error',
58
+ 'No CLI entry point found.\nPlease add "nan0web.cli.entry" to package.json\nOR create src/cli.js / src/messages/index.js'
59
+ )
60
+ process.exit(1)
61
+ }
62
+
63
+ // 3. Import App Module (D1)
64
+ const appModule = await import(pathToFileURL(appPath))
65
+
66
+ // 4. Resolve Messages (E1 + E2)
67
+ const App = appModule.default || appModule.Messages || appModule.App
68
+ if (!App) {
69
+ showError('Config Error', `Module at ${appPath} must export default, Messages, or App`)
70
+ process.exit(1)
71
+ }
72
+ const Messages = Array.isArray(App) ? App : [App]
73
+
74
+ // 5. Run CLI
75
+ const cli = new CLI({ Messages, argv: process.argv.slice(2) })
76
+ for await (const output of cli.run()) {
77
+ if (output?.content) {
78
+ output.content.forEach((line) => console.info(line))
79
+ }
80
+ }
81
+ } catch (error) {
82
+ showError('Runtime Error', error.message || String(error))
83
+ if (process.env.DEBUG) console.error(error.stack)
84
+ process.exit(1)
85
+ }
86
+ })()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nan0web/ui-cli",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "NaN•Web UI CLI. Command line interface for One application logic (algorithm) and many UI.",
5
5
  "main": "src/index.js",
6
6
  "types": "types/index.d.ts",
@@ -8,7 +8,8 @@
8
8
  "files": [
9
9
  "src/**/*.js",
10
10
  "!src/**/*.test.js",
11
- "types/**/*.d.ts"
11
+ "types/**/*.d.ts",
12
+ "bin/**/*.js"
12
13
  ],
13
14
  "exports": {
14
15
  ".": {
@@ -57,17 +58,20 @@
57
58
  "author": "ЯRаСлав (YaRaSLove) <support@yaro.page>",
58
59
  "license": "ISC",
59
60
  "devDependencies": {
60
- "@nan0web/db-fs": "1.0.0",
61
61
  "@nan0web/i18n": "^1.0.1",
62
- "@nan0web/log": "1.1.1",
63
62
  "@nan0web/test": "1.1.0",
64
63
  "@types/node": "^24.10.1",
65
64
  "knip": "^5.83.0",
66
65
  "prettier": "^3.8.1"
67
66
  },
67
+ "bin": {
68
+ "nan0cli": "./bin/nan0cli.js"
69
+ },
68
70
  "dependencies": {
71
+ "@nan0web/db-fs": "1.0.0",
69
72
  "@nan0web/event": "^1.0.0",
70
- "@nan0web/ui": "workspace:*",
73
+ "@nan0web/log": "1.1.1",
74
+ "@nan0web/ui": "^1.1.0",
71
75
  "prompts": "^2.4.2"
72
76
  }
73
- }
77
+ }
package/src/README.md.js CHANGED
@@ -110,6 +110,59 @@ function testRender() {
110
110
  assert.equal(pkg.name, '@nan0web/ui-cli')
111
111
  })
112
112
 
113
+ /**
114
+ * @docs
115
+ * ## nan0cli — Universal CLI Runner
116
+ *
117
+ * The `nan0cli` binary provides a universal entry point for any nan0web application.
118
+ * It reads the app's `package.json`, resolves the CLI entry point, and runs commands.
119
+ *
120
+ * ### App Contract
121
+ *
122
+ * Your app must export Messages from its entry point:
123
+ *
124
+ * ```js
125
+ * // E1: Messages Array (recommended)
126
+ * export default [Serve, Dump]
127
+ *
128
+ * // E2: Single Message class (auto-wrapped to array)
129
+ * export default class MyApp { }
130
+ * ```
131
+ *
132
+ * ### Entry Point Resolution
133
+ *
134
+ * `nan0cli` looks for the entry point in this order:
135
+ * 1. `nan0web.cli.entry` field in `package.json`
136
+ * 2. `src/cli.js` (convention)
137
+ * 3. `src/messages/index.js` (legacy)
138
+ *
139
+ * ### Configuration
140
+ *
141
+ * ```json
142
+ * {
143
+ * "nan0web": {
144
+ * "cli": { "entry": "src/cli.js" }
145
+ * }
146
+ * }
147
+ * ```
148
+ */
149
+ it('nan0cli binary is registered', () => {
150
+ assert.ok(pkg.bin?.nan0cli, 'bin.nan0cli must be defined')
151
+ assert.equal(pkg.bin.nan0cli, './bin/nan0cli.js')
152
+ })
153
+
154
+ /**
155
+ * @docs
156
+ *
157
+ * ### Error Handling
158
+ *
159
+ * When no entry point is found, `nan0cli` displays a styled `Alert` error and exits with code 1.
160
+ * All errors are displayed via `Logger` + `Alert` components — never raw `console.log`.
161
+ */
162
+ it('nan0cli is included in package files', () => {
163
+ assert.ok(pkg.files?.includes('bin/**/*.js'), 'bin/**/*.js must be in files array')
164
+ })
165
+
113
166
  /**
114
167
  * @docs
115
168
  * ## Usage (V2 Architecture)
@@ -324,7 +377,9 @@ describe('README.md testing', testRender)
324
377
 
325
378
  describe('Rendering README.md', async () => {
326
379
  const parser = new DocsParser()
327
- const text = String(parser.decode(testRender))
380
+ // Read source as string — Bun strips comments from Function.toString()
381
+ const source = await fs.loadDocument('src/README.md.js', '')
382
+ const text = String(parser.decode(source))
328
383
  await fs.saveDocument('README.md', text)
329
384
 
330
385
  it('document is rendered', async () => {
package/src/index.js CHANGED
@@ -3,6 +3,7 @@ import OutputAdapter from './OutputAdapter.js'
3
3
  import { CancelError } from '@nan0web/ui/core'
4
4
  import CLI from './CLI.js'
5
5
  import CommandParser from './CommandParser.js'
6
+ import Form, { generateForm } from './ui/form.js'
6
7
 
7
8
  // V2 Component Exports
8
9
  import { render } from './core/render.js'
@@ -87,6 +88,11 @@ export {
87
88
  CLI,
88
89
  CommandParser,
89
90
  OutputAdapter,
91
+ Form,
92
+ generateForm,
90
93
  }
91
94
 
95
+ export { default as Command } from './Command.js'
96
+ export { str2argv } from './utils/parse.js'
97
+
92
98
  export default CLiInputAdapter