@nan0web/ui 1.0.4 → 1.3.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 +19 -14
- package/package.json +6 -4
- package/src/App/Command/DepsCommand.js +25 -0
- package/src/App/Core/CoreApp.js +18 -17
- package/src/App/Core/UI.js +12 -19
- package/src/App/Core/Widget.js +6 -10
- package/src/App/Core/index.js +3 -3
- package/src/App/Scenario.js +4 -4
- package/src/App/User/Command/Message.js +2 -29
- package/src/App/User/Command/index.js +3 -4
- package/src/App/User/UserApp.js +30 -23
- package/src/App/User/UserUI.js +2 -2
- package/src/App/User/index.js +2 -2
- package/src/App/index.js +5 -10
- package/src/Component/Process/Input.js +10 -17
- package/src/Component/Process/Process.js +3 -5
- package/src/Component/Process/index.js +2 -2
- package/src/Component/SortableList/SortableList.js +100 -0
- package/src/Component/SortableList/index.js +3 -0
- package/src/Component/Welcome/Input.js +2 -4
- package/src/Component/Welcome/Welcome.js +5 -9
- package/src/Component/Welcome/index.js +2 -2
- package/src/Component/index.js +5 -3
- package/src/Frame/Frame.js +163 -146
- package/src/Frame/Props.js +20 -20
- package/src/Locale.js +17 -18
- package/src/Model/User/User.js +3 -6
- package/src/Model/index.js +1 -1
- package/src/README.md.js +84 -94
- package/src/StdIn.js +8 -12
- package/src/StdOut.js +23 -27
- package/src/View/RenderOptions.js +1 -1
- package/src/View/View.js +42 -38
- package/src/core/Error/CancelError.js +2 -2
- package/src/core/Error/index.js +3 -5
- package/src/core/Flow.js +347 -0
- package/src/core/Form/Form.js +35 -33
- package/src/core/Form/Input.js +29 -14
- package/src/core/Form/Message.js +3 -6
- package/src/core/Form/index.js +4 -8
- package/src/core/InputAdapter.js +4 -6
- package/src/core/Message/Message.js +9 -12
- package/src/core/Message/OutputMessage.js +19 -17
- package/src/core/Message/index.js +2 -2
- package/src/core/OutputAdapter.js +12 -10
- package/src/core/Stream.js +4 -3
- package/src/core/StreamEntry.js +2 -2
- package/src/core/UiAdapter.js +57 -29
- package/src/core/index.js +38 -9
- package/src/functions.js +8 -15
- package/src/index.js +21 -32
- package/types/App/Command/DepsCommand.d.ts +16 -0
- package/types/App/Command/Options.d.ts +37 -40
- package/types/App/Command/index.d.ts +6 -6
- package/types/App/Core/CoreApp.d.ts +2 -2
- package/types/App/Core/UI.d.ts +6 -7
- package/types/App/Core/Widget.d.ts +4 -4
- package/types/App/Core/index.d.ts +3 -3
- package/types/App/Scenario.d.ts +1 -1
- package/types/App/User/Command/Message.d.ts +2 -16
- package/types/App/User/Command/Options.d.ts +29 -29
- package/types/App/User/Command/index.d.ts +2 -3
- package/types/App/User/UserApp.d.ts +5 -5
- package/types/App/User/index.d.ts +2 -2
- package/types/App/index.d.ts +4 -4
- package/types/Component/Process/Process.d.ts +2 -2
- package/types/Component/Process/index.d.ts +2 -2
- package/types/Component/SortableList/SortableList.d.ts +58 -0
- package/types/Component/SortableList/index.d.ts +2 -0
- package/types/Component/Welcome/Input.d.ts +1 -1
- package/types/Component/Welcome/Welcome.d.ts +1 -1
- package/types/Component/Welcome/index.d.ts +2 -2
- package/types/Component/index.d.ts +5 -3
- package/types/Frame/Frame.d.ts +1 -1
- package/types/Frame/Props.d.ts +1 -1
- package/types/Model/index.d.ts +1 -1
- package/types/StdIn.d.ts +2 -2
- package/types/StdOut.d.ts +1 -1
- package/types/View/View.d.ts +7 -7
- package/types/core/Error/index.d.ts +1 -1
- package/types/core/Flow.d.ts +320 -0
- package/types/core/Form/Form.d.ts +2 -2
- package/types/core/Form/Input.d.ts +15 -4
- package/types/core/Form/Message.d.ts +1 -1
- package/types/core/Form/index.d.ts +3 -3
- package/types/core/InputAdapter.d.ts +2 -2
- package/types/core/Intent.d.ts +65 -68
- package/types/core/Message/InputMessage.d.ts +65 -65
- package/types/core/Message/Message.d.ts +1 -1
- package/types/core/Message/OutputMessage.d.ts +1 -1
- package/types/core/Message/index.d.ts +2 -2
- package/types/core/Stream.d.ts +1 -2
- package/types/core/UiAdapter.d.ts +22 -4
- package/types/core/index.d.ts +5 -2
- package/types/index.d.ts +10 -10
package/src/Locale.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { typeOf, notEmpty } from
|
|
1
|
+
import { typeOf, notEmpty } from '@nan0web/types'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Handles locale-specific formatting for different data types.
|
|
@@ -42,14 +42,14 @@ export default class Locale {
|
|
|
42
42
|
*/
|
|
43
43
|
constructor(props = {}) {
|
|
44
44
|
const {
|
|
45
|
-
lang =
|
|
46
|
-
collate =
|
|
47
|
-
ctype =
|
|
48
|
-
messages =
|
|
49
|
-
monetary =
|
|
50
|
-
numeric =
|
|
51
|
-
time =
|
|
52
|
-
all =
|
|
45
|
+
lang = '',
|
|
46
|
+
collate = '',
|
|
47
|
+
ctype = '',
|
|
48
|
+
messages = '',
|
|
49
|
+
monetary = '',
|
|
50
|
+
numeric = '',
|
|
51
|
+
time = '',
|
|
52
|
+
all = 'uk_UA.UTF-8',
|
|
53
53
|
} = props
|
|
54
54
|
this.lang = lang
|
|
55
55
|
this.collate = collate
|
|
@@ -70,9 +70,9 @@ export default class Locale {
|
|
|
70
70
|
format(type, options) {
|
|
71
71
|
if (Number === type || typeOf(Number)(type)) {
|
|
72
72
|
/**
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
* new (locales?: LocalesArgument, options?: NumberFormatOptions): NumberFormat;
|
|
74
|
+
* (locales?: LocalesArgument, options?: NumberFormatOptions): NumberFormat;
|
|
75
|
+
* supportedLocalesOf(locales: LocalesArgument, options?: NumberFormatOptions): string[];
|
|
76
76
|
*/
|
|
77
77
|
/**
|
|
78
78
|
* localeMatcher?: "lookup" | "best fit" | undefined;
|
|
@@ -85,18 +85,18 @@ export default class Locale {
|
|
|
85
85
|
* maximumFractionDigits?: number | undefined;
|
|
86
86
|
* minimumSignificantDigits?: number | undefined;
|
|
87
87
|
* maximumSignificantDigits?: number | undefined;
|
|
88
|
-
|
|
88
|
+
*/
|
|
89
89
|
const locales = [this.numeric, this.all, this.lang].filter(notEmpty)
|
|
90
90
|
return (value) => {
|
|
91
91
|
return new Intl.NumberFormat(locales, options).format(value)
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
-
if (
|
|
94
|
+
if ('string' === typeof type) {
|
|
95
95
|
const locales = [this.monetary, this.numeric, this.all, this.lang].filter(notEmpty)
|
|
96
96
|
return (value) => {
|
|
97
97
|
return new Intl.NumberFormat(locales, {
|
|
98
|
-
style:
|
|
99
|
-
currency: type ===
|
|
98
|
+
style: 'currency',
|
|
99
|
+
currency: type === 'currency' ? options.currency : type,
|
|
100
100
|
...options,
|
|
101
101
|
}).format(value)
|
|
102
102
|
}
|
|
@@ -110,10 +110,9 @@ export default class Locale {
|
|
|
110
110
|
*/
|
|
111
111
|
static from(input) {
|
|
112
112
|
if (input instanceof Locale) return input
|
|
113
|
-
if (
|
|
113
|
+
if ('string' === typeof input) {
|
|
114
114
|
return new Locale({ all: input })
|
|
115
115
|
}
|
|
116
116
|
return new Locale(input)
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
-
|
package/src/Model/User/User.js
CHANGED
|
@@ -15,10 +15,7 @@ class User {
|
|
|
15
15
|
* @param {string} [props.email=""] - User email
|
|
16
16
|
*/
|
|
17
17
|
constructor(props = {}) {
|
|
18
|
-
const {
|
|
19
|
-
name = "",
|
|
20
|
-
email = "",
|
|
21
|
-
} = props
|
|
18
|
+
const { name = '', email = '' } = props
|
|
22
19
|
this.name = String(name)
|
|
23
20
|
this.email = String(email)
|
|
24
21
|
}
|
|
@@ -36,7 +33,7 @@ class User {
|
|
|
36
33
|
* @returns {string} User name and email (if exists)
|
|
37
34
|
*/
|
|
38
35
|
toString() {
|
|
39
|
-
return [this.name, this.email ? `<${this.email}>` :
|
|
36
|
+
return [this.name, this.email ? `<${this.email}>` : ''].filter(Boolean).join(' ')
|
|
40
37
|
}
|
|
41
38
|
|
|
42
39
|
/**
|
|
@@ -46,7 +43,7 @@ class User {
|
|
|
46
43
|
*/
|
|
47
44
|
static from(props) {
|
|
48
45
|
if (props instanceof User) return props
|
|
49
|
-
if (
|
|
46
|
+
if ('string' === typeof props) {
|
|
50
47
|
return new User({ name: props })
|
|
51
48
|
}
|
|
52
49
|
return new User(props)
|
package/src/Model/index.js
CHANGED
package/src/README.md.js
CHANGED
|
@@ -1,29 +1,17 @@
|
|
|
1
|
-
import { describe, it, before, beforeEach } from
|
|
2
|
-
import assert from
|
|
3
|
-
import FS from
|
|
4
|
-
import { NoConsole } from
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
runSpawn,
|
|
9
|
-
} from "@nan0web/test"
|
|
10
|
-
import {
|
|
11
|
-
Frame,
|
|
12
|
-
Model,
|
|
13
|
-
OutputMessage,
|
|
14
|
-
View,
|
|
15
|
-
FormInput,
|
|
16
|
-
UiMessage,
|
|
17
|
-
UiForm,
|
|
18
|
-
} from "./index.js"
|
|
19
|
-
import { Welcome } from "./Component/index.js"
|
|
1
|
+
import { describe, it, before, beforeEach } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import FS from '@nan0web/db-fs'
|
|
4
|
+
import { NoConsole } from '@nan0web/log'
|
|
5
|
+
import { DatasetParser, DocsParser, runSpawn } from '@nan0web/test'
|
|
6
|
+
import { Frame, Model, OutputMessage, View, FormInput, UiMessage, UiForm } from './index.js'
|
|
7
|
+
import { Welcome } from './Component/index.js'
|
|
20
8
|
|
|
21
9
|
const fs = new FS()
|
|
22
10
|
let pkg
|
|
23
11
|
|
|
24
12
|
// Load package.json once before tests
|
|
25
13
|
before(async () => {
|
|
26
|
-
const doc = await fs.loadDocument(
|
|
14
|
+
const doc = await fs.loadDocument('package.json', {})
|
|
27
15
|
pkg = doc || {}
|
|
28
16
|
})
|
|
29
17
|
|
|
@@ -33,13 +21,10 @@ beforeEach((info) => {
|
|
|
33
21
|
console = new NoConsole()
|
|
34
22
|
})
|
|
35
23
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
* the final `README.md`. Keeping the comments here ensures the
|
|
41
|
-
* documentation stays close to the code.
|
|
42
|
-
*/
|
|
24
|
+
// Core test suite that also serves as the source for README generation.
|
|
25
|
+
// The block comments inside each `it` block are extracted to build
|
|
26
|
+
// the final `README.md`. Keeping the comments here ensures the
|
|
27
|
+
// documentation stays close to the code.
|
|
43
28
|
function testRender() {
|
|
44
29
|
/**
|
|
45
30
|
* @docs
|
|
@@ -65,35 +50,35 @@ function testRender() {
|
|
|
65
50
|
*
|
|
66
51
|
* ## Installation
|
|
67
52
|
*/
|
|
68
|
-
it(
|
|
53
|
+
it('How to install with npm?', () => {
|
|
69
54
|
/**
|
|
70
55
|
* ```bash
|
|
71
56
|
* npm install @nan0web/ui
|
|
72
57
|
* ```
|
|
73
58
|
*/
|
|
74
|
-
assert.equal(pkg.name,
|
|
59
|
+
assert.equal(pkg.name, '@nan0web/ui')
|
|
75
60
|
})
|
|
76
61
|
/**
|
|
77
62
|
* @docs
|
|
78
63
|
*/
|
|
79
|
-
it(
|
|
64
|
+
it('How to install with pnpm?', () => {
|
|
80
65
|
/**
|
|
81
66
|
* ```bash
|
|
82
67
|
* pnpm add @nan0web/ui
|
|
83
68
|
* ```
|
|
84
69
|
*/
|
|
85
|
-
assert.equal(pkg.name,
|
|
70
|
+
assert.equal(pkg.name, '@nan0web/ui')
|
|
86
71
|
})
|
|
87
72
|
/**
|
|
88
73
|
* @docs
|
|
89
74
|
*/
|
|
90
|
-
it(
|
|
75
|
+
it('How to install with yarn?', () => {
|
|
91
76
|
/**
|
|
92
77
|
* ```bash
|
|
93
78
|
* yarn add @nan0web/ui
|
|
94
79
|
* ```
|
|
95
80
|
*/
|
|
96
|
-
assert.equal(pkg.name,
|
|
81
|
+
assert.equal(pkg.name, '@nan0web/ui')
|
|
97
82
|
})
|
|
98
83
|
|
|
99
84
|
/**
|
|
@@ -110,16 +95,16 @@ function testRender() {
|
|
|
110
95
|
* Messages are simple, serializable data containers. They help build
|
|
111
96
|
* decoupled communication systems between UI components.
|
|
112
97
|
*/
|
|
113
|
-
it(
|
|
98
|
+
it('How to create input and output messages?', () => {
|
|
114
99
|
//import { InputMessage, OutputMessage } from '@nan0web/ui'
|
|
115
100
|
|
|
116
101
|
const input = UiMessage.from({ body: 'Hello User' })
|
|
117
102
|
const output = OutputMessage.from({ content: ['Welcome to @nan0web/ui'] })
|
|
118
103
|
console.info(input) // ← Message { body: "Hello User", head: {}, id: "....", type: "" }
|
|
119
104
|
console.info(String(output)) // ← Welcome to @nan0web/ui
|
|
120
|
-
assert.deepStrictEqual(console.output()[0][1].body,
|
|
105
|
+
assert.deepStrictEqual(console.output()[0][1].body, 'Hello User')
|
|
121
106
|
assert.deepStrictEqual(console.output()[0][1].head, {})
|
|
122
|
-
assert.deepStrictEqual(console.output()[0][1].type,
|
|
107
|
+
assert.deepStrictEqual(console.output()[0][1].type, '')
|
|
123
108
|
assert.ok(console.output()[0][1].id)
|
|
124
109
|
assert.ok(console.output()[1][1].endsWith('Welcome to @nan0web/ui'))
|
|
125
110
|
})
|
|
@@ -140,27 +125,32 @@ function testRender() {
|
|
|
140
125
|
* - `checkbox`
|
|
141
126
|
* - `textarea`
|
|
142
127
|
*/
|
|
143
|
-
it(
|
|
128
|
+
it('How to define and validate a UiForm?', () => {
|
|
144
129
|
//import { UiForm } from '@nan0web/ui'
|
|
145
130
|
|
|
146
131
|
const form = new UiForm({
|
|
147
|
-
title:
|
|
132
|
+
title: 'Contact Form',
|
|
148
133
|
fields: [
|
|
149
|
-
FormInput.from({ name:
|
|
150
|
-
FormInput.from({
|
|
134
|
+
FormInput.from({ name: 'email', label: 'Email Address', type: 'email', required: true }),
|
|
135
|
+
FormInput.from({
|
|
136
|
+
name: 'message',
|
|
137
|
+
label: 'Your Message',
|
|
138
|
+
type: 'textarea',
|
|
139
|
+
required: true,
|
|
140
|
+
}),
|
|
151
141
|
],
|
|
152
142
|
state: {
|
|
153
|
-
email:
|
|
154
|
-
message:
|
|
155
|
-
}
|
|
143
|
+
email: 'invalid-email',
|
|
144
|
+
message: 'Hello!',
|
|
145
|
+
},
|
|
156
146
|
})
|
|
157
147
|
|
|
158
148
|
const errors = form.validate()
|
|
159
149
|
console.info(errors.size) // ← 1
|
|
160
|
-
console.info(errors.get(
|
|
150
|
+
console.info(errors.get('email')) // ← Invalid email format
|
|
161
151
|
|
|
162
152
|
assert.equal(console.output()[0][1], 1)
|
|
163
|
-
assert.equal(console.output()[1][1],
|
|
153
|
+
assert.equal(console.output()[1][1], 'Invalid email format')
|
|
164
154
|
})
|
|
165
155
|
|
|
166
156
|
/**
|
|
@@ -172,13 +162,13 @@ function testRender() {
|
|
|
172
162
|
* - `Welcome` – greets user by name
|
|
173
163
|
* - `Process` – shows progress bar and time
|
|
174
164
|
*/
|
|
175
|
-
it(
|
|
165
|
+
it('How to render the Welcome component?', () => {
|
|
176
166
|
//import { Welcome } from '@nan0web/ui'
|
|
177
167
|
|
|
178
|
-
const frame = Welcome({ user: { name:
|
|
179
|
-
const firstLine = frame[0].join(
|
|
168
|
+
const frame = Welcome({ user: { name: 'Alice' } })
|
|
169
|
+
const firstLine = frame[0].join('')
|
|
180
170
|
console.info(firstLine) // ← Welcome Alice!
|
|
181
|
-
assert.equal(console.output()[0][1],
|
|
171
|
+
assert.equal(console.output()[0][1], 'Welcome Alice!')
|
|
182
172
|
})
|
|
183
173
|
|
|
184
174
|
/**
|
|
@@ -193,13 +183,13 @@ function testRender() {
|
|
|
193
183
|
* - StdIn / StdOut – input/output streams
|
|
194
184
|
* - Frame – output buffer with visual properties
|
|
195
185
|
*/
|
|
196
|
-
it(
|
|
186
|
+
it('How to render frame with View?', () => {
|
|
197
187
|
//import { View } from '@nan0web/ui'
|
|
198
188
|
|
|
199
189
|
const view = new View()
|
|
200
|
-
view.render(1)([
|
|
190
|
+
view.render(1)(['Hello, world'])
|
|
201
191
|
console.info(String(view.frame)) // ← "\rHello, world"
|
|
202
|
-
assert.ok(String(view.frame).includes(
|
|
192
|
+
assert.ok(String(view.frame).includes('Hello, world'))
|
|
203
193
|
})
|
|
204
194
|
|
|
205
195
|
/**
|
|
@@ -215,36 +205,36 @@ function testRender() {
|
|
|
215
205
|
* - `REPLACE` – erases and replaces full frame area
|
|
216
206
|
* - `VISIBLE` – renders only visible part of frame
|
|
217
207
|
*/
|
|
218
|
-
it(
|
|
208
|
+
it('How to create a Frame with fixed size?', () => {
|
|
219
209
|
//import { Frame } from '@nan0web/ui'
|
|
220
210
|
|
|
221
211
|
const frame = new Frame({
|
|
222
|
-
value: [[
|
|
212
|
+
value: [['Frame content']],
|
|
223
213
|
width: 20,
|
|
224
214
|
height: 5,
|
|
225
215
|
renderMethod: Frame.RenderMethod.APPEND,
|
|
226
216
|
})
|
|
227
217
|
|
|
228
218
|
const rendered = frame.render()
|
|
229
|
-
console.info(rendered.includes(
|
|
230
|
-
assert.ok(rendered.includes(
|
|
219
|
+
console.info(rendered.includes('Frame content')) // ← true
|
|
220
|
+
assert.ok(rendered.includes('Frame content'))
|
|
231
221
|
})
|
|
232
|
-
it(
|
|
222
|
+
it('How to create a Frame with different render methods?', () => {
|
|
233
223
|
//import { Frame } from '@nan0web/ui'
|
|
234
224
|
|
|
235
225
|
const frame = new Frame({
|
|
236
|
-
value: [[
|
|
226
|
+
value: [['Frame content']],
|
|
237
227
|
width: 20,
|
|
238
228
|
height: 5,
|
|
239
229
|
})
|
|
240
230
|
|
|
241
231
|
frame.renderMethod = Frame.RenderMethod.REPLACE
|
|
242
232
|
const renderedReplace = frame.render()
|
|
243
|
-
assert.ok(renderedReplace.includes(
|
|
233
|
+
assert.ok(renderedReplace.includes('Frame content'))
|
|
244
234
|
|
|
245
235
|
frame.renderMethod = Frame.RenderMethod.VISIBLE
|
|
246
236
|
const renderedVisible = frame.render()
|
|
247
|
-
assert.ok(renderedVisible.includes(
|
|
237
|
+
assert.ok(renderedVisible.includes('Frame content'))
|
|
248
238
|
})
|
|
249
239
|
|
|
250
240
|
/**
|
|
@@ -255,14 +245,14 @@ function testRender() {
|
|
|
255
245
|
*
|
|
256
246
|
* - `User` – user data
|
|
257
247
|
*/
|
|
258
|
-
it(
|
|
248
|
+
it('How to use a User model?', () => {
|
|
259
249
|
//import { Model } from '@nan0web/ui'
|
|
260
250
|
|
|
261
|
-
const user = new Model.User({ name:
|
|
251
|
+
const user = new Model.User({ name: 'Charlie', email: 'charlie@example.com' })
|
|
262
252
|
console.info(user.name) // ← Charlie
|
|
263
253
|
console.info(user.email) // ← charlie@example.com
|
|
264
|
-
assert.equal(user.name,
|
|
265
|
-
assert.equal(user.email,
|
|
254
|
+
assert.equal(user.name, 'Charlie')
|
|
255
|
+
assert.equal(user.email, 'charlie@example.com')
|
|
266
256
|
})
|
|
267
257
|
|
|
268
258
|
/**
|
|
@@ -274,15 +264,15 @@ function testRender() {
|
|
|
274
264
|
* All components, adapters, and models are designed to be testable
|
|
275
265
|
* with minimal setup.
|
|
276
266
|
*/
|
|
277
|
-
it(
|
|
267
|
+
it('How to test UI components with assertions?', () => {
|
|
278
268
|
//import { Welcome } from '@nan0web/ui'
|
|
279
269
|
|
|
280
|
-
const output = Welcome({ user: { name:
|
|
270
|
+
const output = Welcome({ user: { name: 'Test' } })
|
|
281
271
|
console.info(output) // ← Welcome Test!
|
|
282
272
|
assert.deepStrictEqual(console.output()[0][1], [
|
|
283
|
-
[
|
|
284
|
-
[
|
|
285
|
-
[
|
|
273
|
+
['Welcome', ' ', 'Test', '!'],
|
|
274
|
+
['What can we do today great?'],
|
|
275
|
+
[''],
|
|
286
276
|
])
|
|
287
277
|
})
|
|
288
278
|
|
|
@@ -299,7 +289,7 @@ function testRender() {
|
|
|
299
289
|
*
|
|
300
290
|
* Run to explore live functionality:
|
|
301
291
|
*/
|
|
302
|
-
it(
|
|
292
|
+
it('How to run the playground?', async () => {
|
|
303
293
|
/**
|
|
304
294
|
* ```bash
|
|
305
295
|
* # Clone repository and run playground
|
|
@@ -309,10 +299,10 @@ function testRender() {
|
|
|
309
299
|
* npm run play
|
|
310
300
|
* ```
|
|
311
301
|
*/
|
|
312
|
-
assert.ok(String(pkg.scripts?.play).includes(
|
|
313
|
-
const response = await runSpawn(
|
|
314
|
-
assert.ok(response.code === 0,
|
|
315
|
-
assert.ok(response.text.trim().endsWith(
|
|
302
|
+
assert.ok(String(pkg.scripts?.play).includes('node play'))
|
|
303
|
+
const response = await runSpawn('git', ['remote', 'get-url', 'origin'])
|
|
304
|
+
assert.ok(response.code === 0, 'git command fails (e.g., not in a git repo)')
|
|
305
|
+
assert.ok(response.text.trim().endsWith(':nan0web/ui.git'))
|
|
316
306
|
})
|
|
317
307
|
|
|
318
308
|
/**
|
|
@@ -332,39 +322,39 @@ function testRender() {
|
|
|
332
322
|
*
|
|
333
323
|
* ## Contributing
|
|
334
324
|
*/
|
|
335
|
-
it(
|
|
336
|
-
assert.equal(pkg.scripts?.precommit,
|
|
337
|
-
assert.equal(pkg.scripts?.prepush,
|
|
338
|
-
assert.equal(pkg.scripts?.prepare,
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
assert.ok(str.includes("# Contributing"))
|
|
325
|
+
it('How to contribute? - [check here](./CONTRIBUTING.md)', async () => {
|
|
326
|
+
assert.equal(pkg.scripts?.precommit, 'npm test')
|
|
327
|
+
assert.equal(pkg.scripts?.prepush, 'npm test')
|
|
328
|
+
assert.equal(pkg.scripts?.prepare, 'husky')
|
|
329
|
+
const str = await fs.loadDocumentAs('.txt', 'CONTRIBUTING.md')
|
|
330
|
+
assert.ok(str.includes('# Contributing'))
|
|
342
331
|
})
|
|
343
332
|
|
|
344
333
|
/**
|
|
345
334
|
* @docs
|
|
346
335
|
* ## License
|
|
347
336
|
*/
|
|
348
|
-
it(
|
|
337
|
+
it('How to license ISC? - [check here](./LICENSE)', async () => {
|
|
349
338
|
/** @docs */
|
|
350
|
-
const text = await fs.
|
|
351
|
-
assert.ok(
|
|
339
|
+
const text = await fs.loadDocumentAs('.txt', 'LICENSE')
|
|
340
|
+
assert.ok(text.includes('ISC'))
|
|
352
341
|
})
|
|
353
342
|
}
|
|
354
343
|
|
|
355
|
-
describe(
|
|
344
|
+
describe('README.md testing', testRender)
|
|
356
345
|
|
|
357
|
-
describe(
|
|
358
|
-
let text =
|
|
359
|
-
const format = new Intl.NumberFormat(
|
|
346
|
+
describe('Rendering README.md', async () => {
|
|
347
|
+
let text = ''
|
|
348
|
+
const format = new Intl.NumberFormat('en-US').format
|
|
360
349
|
const parser = new DocsParser()
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
350
|
+
const source = await fs.loadDocument('src/README.md.js')
|
|
351
|
+
text = String(parser.decode(source))
|
|
352
|
+
await fs.saveDocument('README.md', { content: text })
|
|
353
|
+
const dataset = DatasetParser.parse(text, pkg?.name ?? '@nan0web/ui')
|
|
354
|
+
await fs.saveDocument('.datasets/README.dataset.jsonl', dataset)
|
|
365
355
|
|
|
366
356
|
it(`document is rendered in README.md [${format(Buffer.byteLength(text))}b]`, async () => {
|
|
367
|
-
const text = await fs.
|
|
368
|
-
assert.ok(text.includes(
|
|
357
|
+
const text = await fs.loadDocumentAs('.txt', 'README.md')
|
|
358
|
+
assert.ok(text.includes('## License'))
|
|
369
359
|
})
|
|
370
360
|
})
|
package/src/StdIn.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import EventProcessor from
|
|
2
|
-
import { typeOf } from
|
|
3
|
-
import { UiMessage } from
|
|
1
|
+
import EventProcessor from '@nan0web/event/oop'
|
|
2
|
+
import { typeOf } from '@nan0web/types'
|
|
3
|
+
import { UiMessage } from './core/index.js'
|
|
4
4
|
|
|
5
|
-
class Processor extends EventProcessor {
|
|
5
|
+
class Processor extends EventProcessor {}
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Handles standard input stream with message buffering.
|
|
@@ -12,7 +12,7 @@ export default class StdIn extends EventProcessor {
|
|
|
12
12
|
static READ_INTERVAL = 99
|
|
13
13
|
|
|
14
14
|
/** @type {string[]} Messages to ignore */
|
|
15
|
-
static IGNORE_MESSAGES = [
|
|
15
|
+
static IGNORE_MESSAGES = ['', 'undefined']
|
|
16
16
|
|
|
17
17
|
/** @type {UiMessage[]} Input message buffer */
|
|
18
18
|
stream = []
|
|
@@ -28,13 +28,10 @@ export default class StdIn extends EventProcessor {
|
|
|
28
28
|
*/
|
|
29
29
|
constructor(props = {}) {
|
|
30
30
|
super()
|
|
31
|
-
const {
|
|
32
|
-
processor = new Processor(),
|
|
33
|
-
stream = [],
|
|
34
|
-
} = props
|
|
31
|
+
const { processor = new Processor(), stream = [] } = props
|
|
35
32
|
this.processor = processor
|
|
36
33
|
this.stream = stream
|
|
37
|
-
this.processor?.on(
|
|
34
|
+
this.processor?.on('data', (data) => {
|
|
38
35
|
this.write(data)
|
|
39
36
|
})
|
|
40
37
|
}
|
|
@@ -62,7 +59,7 @@ export default class StdIn extends EventProcessor {
|
|
|
62
59
|
*/
|
|
63
60
|
async read() {
|
|
64
61
|
while (this.ended) {
|
|
65
|
-
await new Promise(resolve => setTimeout(resolve, StdIn.READ_INTERVAL))
|
|
62
|
+
await new Promise((resolve) => setTimeout(resolve, StdIn.READ_INTERVAL))
|
|
66
63
|
}
|
|
67
64
|
return this.stream.shift() ?? new UiMessage()
|
|
68
65
|
}
|
|
@@ -105,4 +102,3 @@ export default class StdIn extends EventProcessor {
|
|
|
105
102
|
return new this(input)
|
|
106
103
|
}
|
|
107
104
|
}
|
|
108
|
-
|
package/src/StdOut.js
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
import { typeOf } from
|
|
2
|
-
import EventProcessor from
|
|
1
|
+
import { typeOf } from '@nan0web/types'
|
|
2
|
+
import EventProcessor from '@nan0web/event/oop'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Handles standard output stream with formatting capabilities.
|
|
6
6
|
*/
|
|
7
7
|
class StdOut extends EventProcessor {
|
|
8
8
|
/** @type {string} End of line character */
|
|
9
|
-
static EOL =
|
|
9
|
+
static EOL = '\n'
|
|
10
10
|
|
|
11
11
|
/** @type {string} Beginning of line character */
|
|
12
|
-
static BOL =
|
|
12
|
+
static BOL = '\r'
|
|
13
13
|
|
|
14
14
|
/** @type {string} Reset formatting escape code */
|
|
15
|
-
static RESET =
|
|
15
|
+
static RESET = '\x1b[0m'
|
|
16
16
|
|
|
17
17
|
/** @type {string} Clear screen escape code */
|
|
18
|
-
static CLEAR =
|
|
18
|
+
static CLEAR = '\x1b[2J\x1b[H'
|
|
19
19
|
|
|
20
20
|
/** @type {object} Color escape codes */
|
|
21
21
|
static COLORS = {
|
|
22
|
-
red:
|
|
23
|
-
green:
|
|
24
|
-
yellow:
|
|
25
|
-
blue:
|
|
26
|
-
magenta:
|
|
27
|
-
cyan:
|
|
28
|
-
white:
|
|
29
|
-
gray:
|
|
30
|
-
black:
|
|
22
|
+
red: '\x1b[31m',
|
|
23
|
+
green: '\x1b[32m',
|
|
24
|
+
yellow: '\x1b[33m',
|
|
25
|
+
blue: '\x1b[34m',
|
|
26
|
+
magenta: '\x1b[35m',
|
|
27
|
+
cyan: '\x1b[36m',
|
|
28
|
+
white: '\x1b[37m',
|
|
29
|
+
gray: '\x1b[90m',
|
|
30
|
+
black: '\x1b[30m',
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/** @type {Record<string, string>} Style escape codes */
|
|
34
34
|
static STYLES = {
|
|
35
|
-
dim:
|
|
36
|
-
bold:
|
|
37
|
-
underline:
|
|
35
|
+
dim: '\x1b[2m',
|
|
36
|
+
bold: '\x1b[1m',
|
|
37
|
+
underline: '\x1b[4m',
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
@@ -59,11 +59,7 @@ class StdOut extends EventProcessor {
|
|
|
59
59
|
*/
|
|
60
60
|
constructor(props = {}) {
|
|
61
61
|
super()
|
|
62
|
-
const {
|
|
63
|
-
processor,
|
|
64
|
-
stream = [],
|
|
65
|
-
windowSize = [144, 33],
|
|
66
|
-
} = props
|
|
62
|
+
const { processor, stream = [], windowSize = [144, 33] } = props
|
|
67
63
|
this.processor = processor
|
|
68
64
|
this.stream = stream
|
|
69
65
|
this.windowSize = windowSize
|
|
@@ -75,16 +71,16 @@ class StdOut extends EventProcessor {
|
|
|
75
71
|
* @param {any} output - Output to write
|
|
76
72
|
* @param {Function} onError - Error handler callback
|
|
77
73
|
*/
|
|
78
|
-
write(output, onError = v => 1) {
|
|
79
|
-
|
|
74
|
+
write(output, onError = (v) => 1) {
|
|
75
|
+
/**
|
|
80
76
|
* @todo manage the
|
|
81
77
|
*/
|
|
82
78
|
if (typeOf(Array)(output)) {
|
|
83
|
-
output = output.join(
|
|
79
|
+
output = output.join('\n')
|
|
84
80
|
}
|
|
85
81
|
this.stream.push(output)
|
|
86
82
|
this.processor?.write(output, onError)
|
|
87
|
-
this.emit(
|
|
83
|
+
this.emit('data', output)
|
|
88
84
|
}
|
|
89
85
|
|
|
90
86
|
/**
|