@nan0web/ui 1.9.0 → 1.11.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.
Files changed (108) hide show
  1. package/README.md +97 -12
  2. package/package.json +54 -25
  3. package/src/App/Command/DepsCommand.js +3 -4
  4. package/src/Frame/Props.js +12 -18
  5. package/src/InterfaceTemplate/InterfaceTemplate.js +9 -7
  6. package/src/Model/index.js +86 -2
  7. package/src/StdIn.js +2 -6
  8. package/src/cli.js +1 -0
  9. package/src/core/Form/Form.js +8 -7
  10. package/src/core/Form/Message.js +1 -1
  11. package/src/core/GeneratorRunner.js +77 -7
  12. package/src/core/InputAdapter.js +3 -1
  13. package/src/core/Intent.js +214 -16
  14. package/src/core/IntentErrorModel.js +6 -1
  15. package/src/core/Message/Message.js +4 -7
  16. package/src/core/Message/OutputMessage.js +4 -9
  17. package/src/core/Stream.js +16 -5
  18. package/src/core/StreamEntry.js +20 -28
  19. package/src/core/index.js +2 -1
  20. package/src/domain/Content.js +196 -0
  21. package/src/domain/Document.js +17 -0
  22. package/src/domain/FooterModel.js +37 -19
  23. package/src/domain/HeaderModel.js +47 -21
  24. package/src/domain/HeroModel.js +24 -22
  25. package/src/domain/LayoutModel.js +43 -0
  26. package/src/domain/ModelAsApp.js +46 -0
  27. package/src/domain/SandboxModel.js +19 -16
  28. package/src/domain/app/GalleryCommand.js +53 -0
  29. package/src/domain/app/GalleryRenderIntent.js +77 -0
  30. package/src/domain/app/SnapshotAuditor.js +401 -0
  31. package/src/domain/app/SnapshotRunner.js +264 -0
  32. package/src/domain/app/UIApp.js +78 -0
  33. package/src/domain/components/BreadcrumbModel.js +10 -6
  34. package/src/domain/components/FeatureGridModel.js +62 -0
  35. package/src/domain/components/MarkdownModel.js +24 -0
  36. package/src/domain/components/ShellModel.js +243 -0
  37. package/src/domain/components/TableModel.js +10 -6
  38. package/src/domain/components/ToastModel.js +10 -6
  39. package/src/domain/components/index.js +3 -1
  40. package/src/domain/index.js +14 -4
  41. package/src/index.js +21 -2
  42. package/src/inspect.js +2 -0
  43. package/src/test/ScenarioAdapter.js +59 -0
  44. package/src/test/ScenarioTest.js +51 -0
  45. package/src/test/ScenarioTest.story.js +56 -0
  46. package/src/testing/CrashReporter.js +56 -0
  47. package/src/testing/GalleryGenerator.js +29 -0
  48. package/src/testing/LogicInspector.js +55 -0
  49. package/src/testing/SnapshotRunner.js +22 -0
  50. package/src/testing/SpecAdapter.js +115 -0
  51. package/src/testing/SpecRunner.js +121 -0
  52. package/src/testing/VisualAdapter.js +46 -0
  53. package/src/testing/index.js +7 -0
  54. package/src/testing/verifySnapshot.js +17 -0
  55. package/types/App/Command/DepsCommand.d.ts +0 -2
  56. package/types/Model/index.d.ts +56 -4
  57. package/types/StdIn.d.ts +3 -3
  58. package/types/cli.d.ts +1 -0
  59. package/types/core/Form/Form.d.ts +2 -2
  60. package/types/core/GeneratorRunner.d.ts +18 -1
  61. package/types/core/InputAdapter.d.ts +2 -1
  62. package/types/core/Intent.d.ts +232 -26
  63. package/types/core/IntentErrorModel.d.ts +4 -0
  64. package/types/core/Message/Message.d.ts +2 -2
  65. package/types/core/Message/OutputMessage.d.ts +0 -2
  66. package/types/core/index.d.ts +2 -1
  67. package/types/domain/Content.d.ts +340 -0
  68. package/types/domain/Document.d.ts +21 -0
  69. package/types/domain/FooterModel.d.ts +22 -12
  70. package/types/domain/HeaderModel.d.ts +36 -13
  71. package/types/domain/HeroModel.d.ts +19 -17
  72. package/types/domain/LayoutModel.d.ts +34 -0
  73. package/types/domain/ModelAsApp.d.ts +23 -0
  74. package/types/domain/SandboxModel.d.ts +10 -0
  75. package/types/domain/app/GalleryCommand.d.ts +55 -0
  76. package/types/domain/app/GalleryRenderIntent.d.ts +31 -0
  77. package/types/domain/app/SnapshotAuditor.d.ts +99 -0
  78. package/types/domain/app/SnapshotRunner.d.ts +45 -0
  79. package/types/domain/app/UIApp.d.ts +60 -0
  80. package/types/domain/components/BreadcrumbModel.d.ts +6 -8
  81. package/types/domain/components/FeatureGridModel.d.ts +50 -0
  82. package/types/domain/components/MarkdownModel.d.ts +19 -0
  83. package/types/domain/components/ShellModel.d.ts +56 -0
  84. package/types/domain/components/TableModel.d.ts +4 -0
  85. package/types/domain/components/ToastModel.d.ts +4 -0
  86. package/types/domain/components/index.d.ts +3 -0
  87. package/types/domain/index.d.ts +10 -4
  88. package/types/index.d.ts +19 -1
  89. package/types/inspect.d.ts +2 -0
  90. package/types/test/ScenarioAdapter.d.ts +43 -0
  91. package/types/test/ScenarioTest.d.ts +24 -0
  92. package/types/test/ScenarioTest.story.d.ts +1 -0
  93. package/types/testing/CrashReporter.d.ts +13 -0
  94. package/types/testing/GalleryGenerator.d.ts +1 -0
  95. package/types/testing/LogicInspector.d.ts +22 -0
  96. package/types/testing/SnapshotRunner.d.ts +7 -0
  97. package/types/testing/SpecAdapter.d.ts +57 -0
  98. package/types/testing/SpecRunner.d.ts +41 -0
  99. package/types/testing/VisualAdapter.d.ts +9 -0
  100. package/types/testing/index.d.ts +7 -0
  101. package/types/testing/verifySnapshot.d.ts +14 -0
  102. package/src/README.md.js +0 -436
  103. package/types/App/Command/Options.d.ts +0 -43
  104. package/types/App/Command/index.d.ts +0 -8
  105. package/types/App/User/Command/Options.d.ts +0 -34
  106. package/types/core/Message/InputMessage.d.ts +0 -71
  107. package/types/domain/components/HeroModel.d.ts +0 -24
  108. package/types/domain/components/ShowcaseAppModel.d.ts +0 -32
package/README.md CHANGED
@@ -90,9 +90,9 @@ const form = new UiForm({
90
90
  message: 'Hello!',
91
91
  },
92
92
  })
93
- const errors = form.validate()
94
- console.info(errors.size) // ← 1
95
- console.info(errors.get('email')) // ← Invalid email format
93
+ const { isValid, errors } = form.validate()
94
+ console.info(Object.keys(errors).length) // ← 1
95
+ console.info(errors.email) // ← Invalid email format
96
96
  ```
97
97
  ### Components
98
98
 
@@ -148,27 +148,112 @@ const frame = new Frame({
148
148
  const rendered = frame.render()
149
149
  console.info(rendered.includes('Frame content')) // ← true
150
150
  ```
151
- ### Models
151
+ ### Domain Models (v1.9.0)
152
152
 
153
- UI models are plain data objects managed by `Model` classes.
153
+ v1.9.0 introduces a comprehensive set of domain models for layout and components.
154
+ These models follow the **Model-as-Schema** pattern.
154
155
 
155
- - `User` – user data
156
+ #### Layout Models
157
+ - `HeaderModel` — title, logo, navigation actions
158
+ - `FooterModel` — copyright, version, social links
159
+ - `HeroModel` — prominent call-to-action
156
160
 
157
- How to use a User model?
161
+ #### HTML5 Base Elements
162
+ Fully typed zero-cost support for standard tags: `div`, `span`, `p`, `h1`-`h6`, `a`, `ul`, `table`, etc., plus SVG basics (`svg`, `path`, `rect`). Data must be standard `camelCase`.
163
+
164
+ #### Component Models
165
+ - `PricingModel` / `PricingSection` — plans with features and prices
166
+ - `FeatureGrid` — grid of feature highlights
167
+ - `ProfileDropdown` — user avatar and settings menu
168
+ - `CommentModel` & `TestimonialModel` — social proof
169
+ - `StatsModel` — data visualizations
170
+ - `TimelineModel` — event history
171
+
172
+ How to use the new Header and Hero models?
158
173
  ```js
159
174
  import { Model } from '@nan0web/ui'
160
- const user = new Model.User({ name: 'Charlie', email: 'charlie@example.com' })
161
- console.info(user.name) // Charlie
162
- console.info(user.email) // ← charlie@example.com
175
+ const { HeaderModel, HeroModel } = Model
176
+ const header = new HeaderModel({
177
+ title: 'NaN•Web',
178
+ logo: '/logo.svg',
179
+ actions: [{ title: 'Docs', href: '/docs' }],
180
+ })
181
+ const hero = new HeroModel({
182
+ title: 'One Logic — Many UI',
183
+ actions: [{ title: 'Get Started', href: '/start' }],
184
+ })
185
+ console.info(header.title) // ← NaN•Web
186
+ console.info(hero.actions[0].title) // ← Get Started
163
187
  ```
164
- ### Testing UI
188
+ ### Intent Generators (v1.11.0)
189
+
190
+ From v1.11.0, Intent creators are standard named functions generating
191
+ strict interactions (ask, progress, show, render, result).
192
+
193
+ - `ask(field, schema)` — requests input from the environment.
194
+ - `progress(message)` — updates a visual loader.
195
+ - `show(message, level, data)` — displays a notification (replaces deprecated `log`).
196
+ - `render(component, props)` — renders a specific component view.
197
+ - `result(data)` — ends the model execution cleanly.
198
+
199
+ How to use Intent generators? (v1.11.0)
200
+ ```js
201
+ import { ask, show, result } from '@nan0web/ui'
202
+ const nameIntent = ask('name', { help: 'Your name' })
203
+ const msgIntent = show('Processing...', 'info')
204
+ const endIntent = result({ ok: true })
205
+ ```
206
+ ### Testing UI (v1.11.0 Deterministic Testing)
165
207
 
166
208
  Core unit-tested to ensure stability in different environments.
209
+ With **v1.11.0**, the architecture formally introduces `ScenarioTest` for zero-I/O deterministic testing.
210
+
211
+ By lifting the asynchronous logic and providing an explicit scenario array, models are evaluated instantly without waiting on user prompt delays.
167
212
 
213
+ How to test Model pipelines deterministically?
214
+ ```js
215
+ import { ModelAsApp, ask, result, show } from '@nan0web/ui'
216
+ import { ScenarioTest } from '@nan0web/ui/test/ScenarioTest.js'
217
+ const { ModelAsApp, ask, result, show } = await import('./index.js')
218
+ const { ScenarioTest } = await import('./test/ScenarioTest.js')
219
+ class ShoppingCartApp extends ModelAsApp {
220
+ *run() {
221
+ const product = yield ask('product', { help: 'Select product' })
222
+ if (product?.value === 'laptop') {
223
+ yield show('Good choice!', 'ok')
224
+ }
225
+ const confirm = yield ask('confirm', { help: 'Confirm purchase?' })
226
+ return result({ product: product?.value, confirm: confirm?.value })
227
+ }
228
+ }
229
+ const res = await ScenarioTest.run(ShoppingCartApp, [
230
+ { field: 'product', value: 'laptop' },
231
+ { field: 'confirm', value: true }
232
+ ])
233
+ ```
234
+ You can also verify exceptions and validation rules by observing the final error in ScenarioTest.
235
+
236
+ How to test validation errors with ScenarioTest?
237
+ ```js
238
+ import { ModelAsApp, ask, result } from '@nan0web/ui'
239
+ import { ScenarioTest } from '@nan0web/ui/test/ScenarioTest.js'
240
+ const { ModelAsApp, ask, result } = await import('./index.js')
241
+ const { ScenarioTest } = await import('./test/ScenarioTest.js')
242
+ class ValidatedApp extends ModelAsApp {
243
+ *run() {
244
+ const code = yield ask('code', { help: 'Enter code', required: true })
245
+ if (!code?.value) throw new Error('Code is mandatory')
246
+ return result({ code: code?.value })
247
+ }
248
+ }
249
+ const res = await ScenarioTest.run(ValidatedApp, [
250
+ { field: 'code', value: '' } // Simulating empty response
251
+ ])
252
+ ```
168
253
  All components, adapters, and models are designed to be testable
169
254
  with minimal setup.
170
255
 
171
- How to test UI components with assertions?
256
+ How to test visual UI components with assertions?
172
257
  ```js
173
258
  import { Welcome } from '@nan0web/ui'
174
259
  const output = Welcome({ user: { name: 'Test' } })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nan0web/ui",
3
- "version": "1.9.0",
3
+ "version": "1.11.0",
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",
@@ -8,6 +8,7 @@
8
8
  "files": [
9
9
  "src/**/*.js",
10
10
  "!src/**/*.test.js",
11
+ "!src/README.md.js",
11
12
  "types/**/*.d.ts"
12
13
  ],
13
14
  "exports": {
@@ -24,25 +25,42 @@
24
25
  "types": "./types/core/index.d.ts"
25
26
  },
26
27
  "./domain": {
27
- "import": "./src/domain/index.js"
28
+ "import": "./src/domain/index.js",
29
+ "types": "./types/domain/index.d.ts"
30
+ },
31
+ "./inspect": {
32
+ "import": "./src/inspect.js",
33
+ "types": "./types/inspect.d.ts"
34
+ },
35
+ "./testing": {
36
+ "import": "./src/testing/index.js",
37
+ "types": "./types/testing/index.d.ts"
28
38
  },
29
39
  "./cli-app": "./apps/cli/src/index.js",
30
40
  "./mobile-app": "./apps/mobile/src/index.js",
31
- "./web-app": "./apps/web/src/App.jsx"
41
+ "./web-app": "./apps/web/src/App.jsx",
42
+ "./ui/cli": "./src/cli.js"
32
43
  },
33
44
  "keywords": [
34
45
  "ui",
35
46
  "nanoweb"
36
47
  ],
48
+ "nan0web": {
49
+ "workflowDir": "docs/{locale}/workflows",
50
+ "workflows": [
51
+ "olm-ui-architecture.md",
52
+ "olm-ui-architecture-core.md",
53
+ "olm-ui-architecture-adapters.md",
54
+ "forge-component.md",
55
+ "interface-welding.md"
56
+ ],
57
+ "inspectors": [
58
+ "./src/domain/app/SnapshotAuditor.js"
59
+ ]
60
+ },
37
61
  "author": "YaRaSLove (ЯRаСлав) <support@yaro.page>",
38
62
  "license": "ISC",
39
63
  "devDependencies": {
40
- "@nan0web/event": "^1.0.1",
41
- "@nan0web/i18n": "^1.1.0",
42
- "@nan0web/release": "^1.0.2",
43
- "@nan0web/test": "^1.1.3",
44
- "@nan0web/ui-cli": "^2.9.0",
45
- "@nan0web/ui-lit": "^1.1.0",
46
64
  "@playwright/test": "^1.58.2",
47
65
  "@rollup/plugin-yaml": "^4.1.2",
48
66
  "@vitest/coverage-v8": "^3.2.4",
@@ -51,40 +69,51 @@
51
69
  "knip": "^5.86.0",
52
70
  "lit": "^3.3.2",
53
71
  "vite": "^6.4.1",
54
- "vitest": "^3.2.4"
72
+ "vitest": "^3.2.4",
73
+ "@nan0web/db-fs": "1.2.1",
74
+ "@nan0web/event": "1.0.1",
75
+ "@nan0web/i18n": "1.5.0",
76
+ "@nan0web/inspect": "1.0.0",
77
+ "@nan0web/ui-cli": "2.12.3",
78
+ "@nan0web/test": "1.1.4",
79
+ "@nan0web/icons": "1.1.0",
80
+ "@nan0web/release": "1.0.3",
81
+ "@nan0web/nan0web.app": "0.1.0",
82
+ "@nan0web/ui-lit": "1.1.0"
55
83
  },
56
84
  "dependencies": {
57
- "@nan0web/co": "^2.0.0",
58
- "@nan0web/log": "^1.1.1",
59
- "@nan0web/types": "^1.4.0",
60
85
  "string-width": "^7.2.0",
61
- "@nan0web/core": "1.1.2"
86
+ "@nan0web/co": "2.0.1",
87
+ "@nan0web/log": "1.1.1",
88
+ "@nan0web/types": "1.7.2",
89
+ "@nan0web/core": "1.1.3"
62
90
  },
63
91
  "scripts": {
92
+ "prebuild": "rm -rf types/",
64
93
  "build": "tsc",
65
94
  "play": "node play/main.js",
66
- "test:unit": "node --test --test-timeout=3333 \"src/**/*.test.js\"",
67
- "test": "npm run test:unit && npm run test:ssg && npm run build",
95
+ "test:unit": "node --test --test-timeout=3333 \"src/*.test.js\" \"src/core/**/*.test.js\" \"src/domain/**/*.test.js\" \"src/test/*.test.js\" \"src/testing/**/*.test.js\"",
96
+ "test": "npm run test:unit && npm run test:audit && npm run build",
68
97
  "test:nan0test": "node --test --test-timeout=3333 \"src/**/*.test.js\" | nan0test parse --fail",
69
98
  "test:coverage": "node --experimental-test-coverage --test-coverage-include=\"src/**/*.js\" --test-coverage-exclude=\"src/**/*.test.js\" --test \"src/**/*.test.js\"",
70
99
  "test:coverage:collect": "nan0test coverage",
71
100
  "test:docs": "node --test src/README.md.js",
72
- "release:spec": "node --test \"releases/**/*.spec.js\"",
73
- "test:release": "node --test \"releases/**/*.test.js\"",
101
+ "release:spec": "node --test \"src/test/releases/**/*.test.js\"",
102
+ "test:release": "node --test \"src/test/releases/**/*.test.js\"",
74
103
  "test:status": "nan0test status --hide-name",
75
104
  "test:play": "node --test --test-timeout=3333 \"play/**/*.test.js\"",
76
- "test:ssg": "node --test --test-timeout=5000 \"src/test/ssg.test.js\"",
77
- "test:e2e": "playwright test --ignore-snapshots test/e2e/components.spec.js test/e2e/debug-label.spec.js",
78
- "test:e2e:slow": "E2E_SLOW=1 playwright test test/e2e/visual.spec.js",
79
- "test:all": "npm run test && npm run test:docs && npm run test:play && npm run test:e2e && npm run knip",
105
+ "test:snapshots": "node src/testing/GalleryGenerator.js",
106
+ "test:gallery": "npm run test:snapshots && npx nan0gallery --dir=snapshots/core",
107
+ "test:audit": "nan0cli audit --dir snapshots/core --data .",
108
+ "test:stories": "node --test \"src/**/*.story.js\"",
109
+ "test:all": "npm run test && npm run test:docs && npm run test:play && npm run test:stories && npm run knip",
80
110
  "knip": "knip --production",
81
111
  "precommit": "npm test",
82
112
  "prepush": "npm test",
83
113
  "release": "nan0release publish",
84
114
  "clean": "rm -rf .cache/ && rm -rf dist/",
85
115
  "clean:modules": "rm -rf node_modules",
86
- "docs:build-data": "node docs/site/scripts/generate-data.js",
87
- "docs:dev": "node docs/site/scripts/generate-data.js && node docs/site/scripts/generate-pages.js && vite docs/site",
88
- "docs:build": "node docs/site/scripts/generate-data.js && node docs/site/scripts/generate-pages.js && vite build docs/site"
116
+ "docs:dev": "nan0web run --data docs --index README --port 4270",
117
+ "docs:build": "nan0web build --data docs --index README --locale uk"
89
118
  }
90
119
  }
@@ -1,23 +1,22 @@
1
1
  import UiMessage from '../../core/Message/Message.js'
2
2
 
3
3
  class DepsCommandBody {
4
- fix = false
5
4
  static fix = {
6
5
  help: 'Fix dependencies',
7
6
  defaultValue: false,
8
7
  }
9
8
  constructor(input = {}) {
10
- const { fix = this.fix } = input
9
+ const { fix = DepsCommandBody.fix.defaultValue } = input
10
+ this.fix = !!fix
11
11
  }
12
12
  }
13
13
 
14
14
  export class DepsCommand extends UiMessage {
15
15
  static Body = DepsCommandBody
16
- /** @type {DepsCommandBody} */
17
- body
18
16
  constructor(input = {}) {
19
17
  const { body = new DepsCommandBody() } = UiMessage.parseBody(input, DepsCommandBody)
20
18
  super(input)
19
+ /** @type {DepsCommandBody} */
21
20
  this.body = body
22
21
  }
23
22
  }
@@ -53,24 +53,6 @@ class FrameProps extends ObjectWithAlias {
53
53
  s: 'strikethrough',
54
54
  }
55
55
 
56
- /** @type {string} Text color */
57
- color = ''
58
-
59
- /** @type {string} Background color */
60
- bgColor = ''
61
-
62
- /** @type {boolean} Bold text flag */
63
- bold = false
64
-
65
- /** @type {boolean} Italic text flag */
66
- italic = false
67
-
68
- /** @type {boolean} Underline text flag */
69
- underline = false
70
-
71
- /** @type {boolean} Strikethrough text flag */
72
- strikethrough = false
73
-
74
56
  /**
75
57
  * @param {object} props - Frame properties
76
58
  * @param {string} [props.color=""] - Text color
@@ -90,11 +72,23 @@ class FrameProps extends ObjectWithAlias {
90
72
  underline = false,
91
73
  strikethrough = false,
92
74
  } = props
75
+
76
+ /** @type {string} Text color */
93
77
  this.color = color
78
+
79
+ /** @type {string} Background color */
94
80
  this.bgColor = bgColor
81
+
82
+ /** @type {boolean} Bold text flag */
95
83
  this.bold = bold
84
+
85
+ /** @type {boolean} Italic text flag */
96
86
  this.italic = italic
87
+
88
+ /** @type {boolean} Underline text flag */
97
89
  this.underline = underline
90
+
91
+ /** @type {boolean} Strikethrough text flag */
98
92
  this.strikethrough = strikethrough
99
93
  }
100
94
  }
@@ -22,13 +22,15 @@ export default class InterfaceTemplate {
22
22
  */
23
23
  static requiredMethods = ['render', 'ask']
24
24
 
25
- /**
26
- * The name of this interface (e.g. 'cli', 'web', 'mobile').
27
- * Override in subclass.
28
- *
29
- * @type {string}
30
- */
31
- name = 'base'
25
+ constructor() {
26
+ /**
27
+ * The name of this interface (e.g. 'cli', 'web', 'mobile').
28
+ * Override in subclass.
29
+ *
30
+ * @type {string}
31
+ */
32
+ this.name = 'base'
33
+ }
32
34
 
33
35
  /**
34
36
  * Render data to the user through the interface.
@@ -1,7 +1,91 @@
1
1
  import User from './User/User.js'
2
+ import { HeaderModel } from '../domain/HeaderModel.js'
3
+ import { FooterModel } from '../domain/FooterModel.js'
4
+ import { HeroModel } from '../domain/HeroModel.js'
5
+ import {
6
+ ButtonModel,
7
+ ConfirmModel,
8
+ InputModel,
9
+ SpinnerModel,
10
+ TableModel,
11
+ ToastModel,
12
+ SelectModel,
13
+ AutocompleteModel,
14
+ TreeModel,
15
+ TabsModel,
16
+ AccordionModel,
17
+ GalleryModel,
18
+ PriceModel,
19
+ PricingModel,
20
+ CommentModel,
21
+ TestimonialModel,
22
+ StatsItemModel,
23
+ StatsModel,
24
+ TimelineItemModel,
25
+ TimelineModel,
26
+ EmptyStateModel,
27
+ BannerModel,
28
+ ProfileDropdownModel,
29
+ } from '../domain/components/index.js'
2
30
 
3
- export { User }
31
+ export {
32
+ User,
33
+ HeaderModel,
34
+ FooterModel,
35
+ HeroModel,
36
+ ButtonModel,
37
+ ConfirmModel,
38
+ InputModel,
39
+ SpinnerModel,
40
+ TableModel,
41
+ ToastModel,
42
+ SelectModel,
43
+ AutocompleteModel,
44
+ TreeModel,
45
+ TabsModel,
46
+ AccordionModel,
47
+ GalleryModel,
48
+ PriceModel,
49
+ PricingModel,
50
+ CommentModel,
51
+ TestimonialModel,
52
+ StatsItemModel,
53
+ StatsModel,
54
+ TimelineItemModel,
55
+ TimelineModel,
56
+ EmptyStateModel,
57
+ BannerModel,
58
+ ProfileDropdownModel,
59
+ }
4
60
 
5
- export default {
61
+ const Model = {
6
62
  User,
63
+ HeaderModel,
64
+ FooterModel,
65
+ HeroModel,
66
+ ButtonModel,
67
+ ConfirmModel,
68
+ InputModel,
69
+ SpinnerModel,
70
+ TableModel,
71
+ ToastModel,
72
+ SelectModel,
73
+ AutocompleteModel,
74
+ TreeModel,
75
+ TabsModel,
76
+ AccordionModel,
77
+ GalleryModel,
78
+ PriceModel,
79
+ PricingModel,
80
+ CommentModel,
81
+ TestimonialModel,
82
+ StatsItemModel,
83
+ StatsModel,
84
+ TimelineItemModel,
85
+ TimelineModel,
86
+ EmptyStateModel,
87
+ BannerModel,
88
+ ProfileDropdownModel,
7
89
  }
90
+
91
+ export default Model
package/src/StdIn.js CHANGED
@@ -14,12 +14,6 @@ export default class StdIn extends EventProcessor {
14
14
  /** @type {string[]} Messages to ignore */
15
15
  static IGNORE_MESSAGES = ['', 'undefined']
16
16
 
17
- /** @type {UiMessage[]} Input message buffer */
18
- stream = []
19
-
20
- /** @type {Processor} Input processor */
21
- processor
22
-
23
17
  /**
24
18
  * Creates a new StdIn instance.
25
19
  * @param {object} props - StdIn properties
@@ -29,7 +23,9 @@ export default class StdIn extends EventProcessor {
29
23
  constructor(props = {}) {
30
24
  super()
31
25
  const { processor = new Processor(), stream = [] } = props
26
+ /** @type {Processor} Input processor */
32
27
  this.processor = processor
28
+ /** @type {UiMessage[]} Input message buffer */
33
29
  this.stream = stream
34
30
  this.processor?.on('data', (data) => {
35
31
  this.write(data)
package/src/cli.js ADDED
@@ -0,0 +1 @@
1
+ export { UIApp as default } from './domain/app/UIApp.js'
@@ -106,10 +106,11 @@ export default class UIForm extends FormMessage {
106
106
  /**
107
107
  * Validates the entire form.
108
108
  *
109
- * @returns {Map<string, string>} Map of validation errors, empty if valid.
109
+ * @returns {any}
110
110
  */
111
111
  validate() {
112
- const errors = new Map()
112
+ /** @type {any} */
113
+ const errors = {}
113
114
  let isValid = true
114
115
 
115
116
  this.fields.forEach((field) => {
@@ -120,7 +121,7 @@ export default class UIForm extends FormMessage {
120
121
  field.required &&
121
122
  (fieldValue === '' || fieldValue === null || fieldValue === undefined)
122
123
  ) {
123
- errors.set(field.name, 'This field is required')
124
+ errors[field.name] = 'This field is required'
124
125
  isValid = false
125
126
  return
126
127
  }
@@ -132,14 +133,12 @@ export default class UIForm extends FormMessage {
132
133
  )
133
134
 
134
135
  if (!fieldValid) {
135
- for (const [key, err] of Object.entries(fieldErrors)) {
136
- errors.set(key, err)
137
- }
136
+ Object.assign(errors, fieldErrors)
138
137
  isValid = false
139
138
  }
140
139
  })
141
140
 
142
- return errors
141
+ return { isValid, errors }
143
142
  }
144
143
 
145
144
  /**
@@ -239,6 +238,8 @@ export default class UIForm extends FormMessage {
239
238
  */
240
239
  toJSON() {
241
240
  return {
241
+ id: this.id,
242
+ type: this.type,
242
243
  time: new Date(this.time).toISOString(),
243
244
  title: this.title,
244
245
  fields: this.fields.map((f) => (f.toJSON ? f.toJSON() : f)),
@@ -14,7 +14,7 @@ export default class FormMessage extends UiMessage {
14
14
  * @param {Object} [input={}] - Message properties.
15
15
  */
16
16
  constructor(input = {}) {
17
- super(input)
17
+ super({ type: 'form', ...input })
18
18
  const { data = {}, schema = {} } = input
19
19
 
20
20
  // Store data and schema for easy access